layout

Using Partial Views - ASP.NET MVC Demystified

When I was writing applications in WebForms (which, thankfully, I no longer do) I often ended up used .ascx files, commonly known as User Controls. Once I moved to MVC, I had to get used to a similar (though not the same) concept: Partial Views.

In this tutorial, we'll walk through a use case for Partial Views and explain what they are and why we need to use them. We'll also walk through the two different render methods: @Html.Partial() and @Html.RenderPartial() and discuss how they are different. Let's get started!

What are Partial Views?

Partial views are .cshtml views that are meant to represent portions of a page that can be used on multiple other views. They are roughly analogous to WebForms' User Controls, but not exactly, mostly because (just like all other views) they don't have a code-behind file.

When you create a partial view, the reason for it to exist is normally that it will be used on many other "parent" views. Let's set up a scenario for this.

Entrees and Ingredients

Say I want to display a list of entrees that I can make for dinner, and along with these entrees I'd like to display the ingredients necessary to make them. In order to do this, I might create classes for Entree and Ingredient like so:

public class Entree  
{
    public int ID { get; set; }
    public string Name { get; set; }
    public List<Ingredient> Ingredients { get; set; }

    public Entree()
    {
        Ingredients = new List<Ingredient>();
    }
}

public class Ingredient  
{
    public string Name { get; set; }
    public decimal Amount { get; set; }
    public string Unit { get; set; }
}

I want to create two views for this: an Index view that will display all the entrees and a Details that will just display one. On each of these views will need to be a list of ingredients, with their units and amounts. Because that display is the same on each view, we can turn that display into a partial view. In our particular case, our partial view will need to have a model bound to it, since it will be displaying the ingredients; of course, it's entirely possible to not use a model on a partial view.

Our partial view (let's call it Ingredients.cshtml) will look like this:

@model List<PartialViewsDemo.Models.Ingredient>

@{
    string display = "";
    foreach (var ingredient in Model)
    {
        display += ingredient.Name + " (" + ingredient.Amount.ToString() + " " + ingredient.Unit + "), ";
    }
    display = display.Remove(display.LastIndexOf(","));
}

<span>@display</span>  

Because this is a partial view, MVC expects it to exist in the Views/Shared folder:

A snapshot of the Views/Shared folder, showing that the Ingredients.cshtml file exists there

Now that we've got our partial view defined, we can define the Entree controller and the Index and Details actions and views. First, here's the Entree controller:

public class EntreeController : Controller  
{
    [HttpGet]
    public ActionResult Index()
    {
        var entrees = EntreeManager.GetAll();
        return View(entrees);
    }

    [HttpGet]
    public ActionResult Details(int id)
    {
        try
        {
            var entree = EntreeManager.GetAll().First(x => x.ID == id);
            return View(entree);
        }
        catch(Exception ex)
        {
            TempData["ErrorMessage"] = "No Entree with ID " + id.ToString() + " exists.";
            return RedirectToAction("Index");
        }
    }
}

In order to use the partial view Ingredients on the Index.cshtml view, we make a call to @Html.Partial(), as shown in the view below:

@model List<PartialViewsDemo.Models.Entree>

@{
    ViewBag.Title = "Index";
}

<h2>All Entrees</h2>

@if(TempData["ErrorMessage"] != null)
{
    <span>@TempData["ErrorMessage"]</span>
}

<table>  
    <thead>
        <tr>
            <th>
                Name
            </th>
            <th>
                Ingredients
            </th>
        </tr>
    </thead>
    <tbody>
        @foreach(var entree in Model)
        {
            <tr>
                <td>
                    @Html.ActionLink(entree.Name, "Details", new { id = entree.ID })
                </td>
                <td>
                    @Html.Partial("Ingredients", entree.Ingredients)
                </td>
            </tr>
        }
    </tbody>
</table>  

On the Details.cshtml view, we make a similar call:

@model PartialViewsDemo.Models.Entree

@{
    ViewBag.Title = "Details";
}

@Html.ActionLink("<< Back to Index", "Index")

<h2>@Model.Name</h2>

<p>Ingredients: @Html.Partial("Ingredients", Model.Ingredients)</p>  

With all of this in place, we can see our completed Index and Details pages.

The Entrees/Index page, showing all three Entrees and the partial call to Ingredients.cshtml

The Entrees/Details/2 page, showing the details for Entree 2 and the partial call to Ingredients.cshtml

An Alternate Rendering Method

In the previous examples we used a call to @Html.Partial() to render the partial views. There is also another call we can make, @Html.RenderPartial(), that will render the partial view in a different manner.

We would use it like this (here's a snippet from the Details view):

<p>Ingredients: @{ Html.RenderPartial("Ingredients", Model.Ingredients); }</p>  

Note that we had to use this call in server-side braces @{}. There's a couple of other differences between @Html.Partial() and @Html.RenderPartial():

  • @Html.Partial() returns a string (meaning you can store the output in a variable), whereas @Html.RenderPartial() returns void and writes directly to the Response.
  • Performance-wise @Html.RenderPartial() will perform better (per an answer on StackOverflow)

I almost always end up using [email protected]() just because I like the syntax better, but the decision is left to you.

Summary

Partial Views in ASP.NET MVC are meant to represent "pieces" of a page that can be rendered in multiple places on different views. They can have models bound to them, and can be rendered using @Html.Partial() or @Html.RenderPartial().

If you'd like to see this demo in a working MVC project, you can download the source from GitHub.

Happy Coding!

What are Layout, _ViewStart, RenderBody, and RenderSection in MVC?

One of ASP.NET MVC's core principles is Separation of Concerns, the idea that different sections of the code are responsible for different things, and their responsibilities don't overlap. This allows MVC to be very change-tolerant, as changes in the Views really shouldn't affect the Model, since they have different concerns.

This paradigm is reflected nicely in the way MVC uses layouts and sections. The architecture is set up so as to separate content from layout, and allow both to be changed without impacting the other. Let's take a look into the Layout engine and see if we can discover how it really works.

_ViewStart

Starting in MVC3, the MVC engine introduced an optional new view called _ViewStart. The primary purpose of _ViewStart is to set values that all the other views will have to use. If this view exists, and it is under the Views folder, it enables us to set properties that apply to all views in the system (check out ScottGu's blog post introducing this feature).

Most commonly, a _ViewStart view will just set some properties. The default one in a new MVC project looks something like this:

@{
    Layout = "~/Views/Shared/_Layout.cshtml";
}

However, we can do some more complex things in ViewStart. For example, say we want to change the layout based on which Controller a user is currently accessing:

@{
    var controller = HttpContext.Current.Request.RequestContext.RouteData.Values["Controller"].ToString();
    if(controller == "Home")
    { 
        Layout = "~/Views/Shared/_Layout.cshtml";
    }
    else
    {
        Layout = "~/Views/Shared/_UserLayout.cshtml";
    }
}

Layout Views

Let's take a look at some of the code from the default _Layout view that Visual Studio gives us when creating a new MVC application:

<body>  
    ...
    <div class="container body-content">
        @RenderBody() 
        <hr />
        <footer>
            <p>&copy; @DateTime.Now.Year - My ASP.NET Application</p>
        </footer>
    </div>

    ...
    @RenderSection("scripts", required: false)
</body>  

The RenderBody and RenderSection calls are special, unique to MVC. They specify the point at which the content from the child view is rendered relative to the layout defined, but they have slightly different uses.

RenderBody

RenderBody is called to render the content of a child view. Any content on said view that is not in a @section will be rendered by RenderBody. Using the Layout view above, that means that all content in a child view will be rendered inside the <div class="container body-content>. This allows us to separate layout that is common to all views from layout that is specific to a single view.

However, what if we need some part of the page to change based on which child view the user is on? That's where RenderSection comes in.

Sections

RenderSection, following with the "separate content from layout" theme, allows us to designate a place for where content will be rendered that is different from RenderBody():

@RenderSection("footer", required: false)

We can designate content to be rendered at RenderSection using a @section declaration.

@section Footer
{
    <p>Section/Index page</p>
}

This allows us to again separate layout from content and provide a flexible framework to build our views.

Note the required: false call in RenderSection. By default, Sections are required, meaning each child view must define the section. We pass required: false to designate that the section is optional.

All of these together give us various ways to separate content from layout, and thereby make maintenance much more extensible in MVC than it was in WebForms (though I may be a little bit biased in that regard).

Happy Coding!