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>© @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
declaration 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.
RenderSection
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
named parameter in RenderSection()
. By default, Sections are required, meaning each child view must define the section. We pass required: false to designate that the specified 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!