Posted on February 15th, 2011
In Part 1 of this post we explored how to create a single shared layout across multiple Areas using the ASPX view engine in ASP.NET MVC 3. In the follow up, we will cover doing the same with the Razor view engine.
Part 2: The Razor view engine
The Razor view engine is different from the ASPX view engine in that there is no concept of a hard line MasterPage. Heck, there is no concept of a user control view (ascx) either. In fact, the Razor view engine keeps things extremely simple. Every view is a cshtml file. The contents of the file define its purpose.
To get started we will need to create a new MVC 3 project, this time selecting Razor as our view engine:
The project that is created will actually come with a _Layout.cshtml
file in the Views/Shared directory that has content indicating that it is a site layout file:
The contents of this file by default are:
@ViewBag.Title
@RenderBody()
It is important to note that the naming convention on this file has no bearing on whether or not it is a site layout. We can rename this file to ViewMasterPage.cshtml
if we wanted to and as long as we reference it as the "layout" file to use for a view page then everything will work fine. How do we reference the "layout" file to use? Lets find out. Time to create our HomeController with an Index action and corresponding view.
If we add a new Razor view using the Add -> View dialog window in Visual Studio and select to use our _Layout.cshtml
file the contents of our view file will be as follows:
@{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
Index
The code block at the top of the view is where we set our variables such as the new ViewBag object properties (similar to the concept of the ViewData collection in previous iterations of ASP.NET MVC). As you can see, there is a variable named Layout that is getting set here as well. If we want to set the "site layout" to use on a per view basis it can be accomplished with the code above.
From here we can follow the same pattern as that of the ASPX view engine and create our Area(s), then simply set the Layout variable in those views to the top level Views/Shared/_Layout.cshtml
file.
@{
Layout = "~/Views/Shared/_Layout.cshtml";
}
Before we take a look at how to implement our unique main menu for each Area(s) that we did in Part 1, lets take a look at a new option available to us with the Razor view engine for making layout management a bit easier. The Razor view engine will look for files with the specific naming convention of _ViewStart.cshtml
that can be used to handle pre-defining variables used by each view within its scope. By default, a new MVC 3 Razor view engine project includes one of these files in the top level Views directory.
The scope of this _ViewStart.cshtml
file covers all views in the top level Views directory:
The default content of this file is:
@{
Layout = "~/Views/Shared/_Layout.cshtml";
}
With this in place, our cshtml view files that we create within the scope of the _ViewStart.cshtml
file can exclude the Layout variable setting if we wish to use the top level site layout file _Layout.cshtml
.
@{
ViewBag.Title = "Index";
}
Index
As we add our Area(s), all we need to do is create a _ViewStart.cshtml
file in the Views directory of our Area and all the Area views will use the Layout value set within it.
Adding an Area specific main menu in the Razor view engine is a little bit different than in the ASPX view engine. For starters, we don't create a user control view file. We just create a new cshtml file that we will name Menu.cshtml
and place that in our top level Views/Shared directory.
We need to give it some content:
- @Html.ActionLink("Go Home", "Index", "Home")
We also need to add a call to RenderPartial in our _Layout.cshtml
file. While we are in there we will make it look like our sample in Part 1 as well.
@ViewBag.Title
Our Test Site
@{Html.RenderPartial("Menu");}
@RenderBody()
And update our Index.cshtml
file to contain the same content as Part 1:
@{
ViewBag.Title = "Home Page";
}
Welcome to our home page!
If we F5 the site from Visual Studio we can see our HomeController Index action rendering our layout, menu and page content just like our ASPX view engine example:
Time to add an Area. We will create a new Area named Articles and create a LatestController
and corresponding view for an Index action. We will also create a view in our Shared directory named Menu.cshtml
for the Area's specific main menu. Finally, we will create the _ViewStart.cshtml
file in the Views directory of our new Area.
The content for our Areas/Articles/Views/Shared/Menu.cshtml
is as follows:
- @Html.ActionLink("Go Home From Articles", "Index", new { area = "", controller = "Home" })
We need to update our Views/Shared/Menu.cshtml
at the top level to add a link into our Articles Area:
- @Html.ActionLink("Go Home", "Index", "Home")
- @Html.ActionLink("Latest Articles", "Index", new { area = "Articles", controller = "Latest" })
We will give our Index.cshtml
view for our LatestController
Index action some content:
@{
ViewBag.Title = "Latest Articles";
}
Check out the latest articles:
Finally, we want to make sure our _ViewStart.cshtml
file that is scoped for our Area contains the correct reference to the top level layout file:
@{
Layout = "~/Views/Shared/_Layout.cshtml";
}
Now if we do an F5 we can see our updated menu on our home page:
...and if we navigate to the Latest Articles we can see our content page with the menu from the Articles Area being used (just like our ASPX view engine example in Part 1):
By creating a Menu.cshtml
View in the Areas/Articles/Views/Shared directory we can control the content that gets used by the call to Html.RenderPartial() from our Razor view engine Layout page. This is explained at the end of Part 1 if you happened to not read that prior to reading Part 2!
As you can see, the Razor view engine is much lighter than the ASPX view engine in terms of content needed in each view file. It is also a bit more simplistic in its approach to view files. But both view engines provide a vary similar way to handle a shared view across Areas.
No new comments are allowed on this post.
Discussion
GundamKnight
Areas FTW!
Antuan
This is very very helpful. I have been searching for a solution for this particular situation for a while now and now I found it. I did see other solutions but they weren't so attractive. Thanks a lot!!!
Justin Schwartzenberger
Dave
Very well explained - keep it up!
Justin Schwartzenberger
Paul Marshall
Well explained article
Justin Schwartzenberger
Shawn Mclean
Dude!! The concept of taking out the menus into partial controls saved me so much time.
Bhash
Thanks for the solution!! It saved my time.
Olufemi Oyekan
Outstanding job, clear as crystal.
Mike Goynes
I just used this (step 2 only) to create my first MVC3 project, and it makes perfect sense. Thanks for creating such a well written explanation.
Teresa
Thank for the solution described above, I've been attempt to add an Area section into an already existing MVC3 razor application.
I've added area and moved over the necessary Views and Controllers, but the views that are now in the Areas sections do not seem to compile any of the HTML Helper methods such as Html.BeginForm() or Html.Encode ect...
I've added
@inherits System.Web.Mvc.WebViewPage
at the top of each view in my areas and also linked in the
@using {namespace}.API.Extensions;
where the HTML helper exists but it still will not compile without errors and will not load any of these views.
Can you tell if I am missing something here?
Thanks in advance!
Teresa
Thank for the solution described above, I've been attempting to add an Area section into an already existing MVC3 razor application.
I've added area and moved over the necessary Views and Controllers, but the views that are now in the Areas sections do not seem to compile any of the HTML Helper methods such as Html.BeginForm() or Html.Encode ect...
I've added
@inherits System.Web.Mvc.WebViewPage
at the top of each view in my areas and also linked in the
@using {namespace}.API.Extensions;
where the HTML helper exists but it still will not compile without errors and will not load any of these views.
Can you tell if I am missing something here?
Thanks in advance!
furkan
Actually i wanna ask a question about adding menus to layout.cshtml from database. I mean i got some menu's names in database and i'm trying to add those menu's names with foreach function but i got error it said that nullreference exception in my model. Could you help me please ?