How do I build an MVC application that leverages Areas for modules but uses a single layout for all Areas?

Posted on February 10th, 2011

Areas in ASP.NET MVC provide a great method for organizing web application tools and sections. Each Area can act as its own isolated MVC block, complete with its own Shared views, Master Pages and Layouts. But what do you do if you want to utilize a single Master Page or Layout across all of your Areas? Lets take a look at how we can accomplish that with the ASPX view engine as well as the Razor view engine.

Part 1: The ASPX view engine

The ASPX view engine utilizes Master Pages and was the primary view engine for MVC 1 and MVC 2. MVC 3 continues to support the ASPX view engine. Using the ASPX view engine, we will build out our single layout design using a top level Master Page and some partial views in our Areas. This technique will work for MVC 2 and MVC 3 applications (where as the Razor technique is only possible in MVC 3). On with the code.

Assuming we start with an Empty MVC 3 project and selected ASPX as our View engine:

Create a new MVC empty project

We will add a new MVC MasterPage view to our Views/Shared directory to start:

Adding a MasterPage

Next we want to create a HomeController and corresponding view directory and Index view:

Home Controller and View

As we stand right now it is pretty easy to use a single layout across our Area(s). All we need to do is reference our top level MasterPage from our Page directives in our Views.

<%@ Page Title="" MasterPageFile="~/Views/Shared/ViewMasterPage.Master" Language="C#" 
    Inherits="System.Web.Mvc.ViewPage" %>

Lets take it a step further and explore how we could handle parts of our single layout that we want to change per Area but not on a per view level. We want each of our Areas to have its own unique main menu. This can be accomplished via partial views both at the top level and in our Areas. We will begin with adding a Partial View to our top level Shared directory and name it Menu.ascx.

Menu partial view

We need to give it some content:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
  • <%: Html.ActionLink("Go Home", "Index", "Home") %>

We also need to add a call to RenderPartial in our MasterPage:

<%@ Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage" %>





    <asp:ContentPlaceHolder ID="TitleContent" runat="server" />


    

Our Test Site

<% Html.RenderPartial("Menu"); %>

And some content to our Index view in our Home/Index directory:

<%@ Page Title="" Language="C#" Inherits="System.Web.Mvc.ViewPage" 
    MasterPageFile="~/Views/Shared/ViewMasterPage.Master" %>

    Home Page


    

Welcome to our home page!

If we F5 the site from Visual Studio we can see our HomeController Index action in action, including our top level menu:

Home Page

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 Partial View in our Shared directory named Menu.ascx.

Area Articles

The content for our Areas/Articles/Views/Shared/Menu.ascx is as follows:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
  • <%: Html.ActionLink("Go Home From Articles", "Index", new { area = "", controller = "Home" })%>

We need to update our Views/Shared/Menu.ascx at the top level to add a link into our Articles Area:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
  • <%: Html.ActionLink("Go Home", "Index", "Home")%>
  • <%: Html.ActionLink("Latest Articles", "Index", new { area="Articles", controller="Latest" }) %>

Finally, we will give our Index view for our LatestController Index action some content:

<%@ Page Title="" Language="C#" Inherits="System.Web.Mvc.ViewPage" 
    MasterPageFile="~/Views/Shared/ViewMasterPage.Master" %>

    Latest Articles


    

Check out the latest articles:

Notice how the Page directive in our view content above is referencing the top level MasterPage (found in Views/Shared). We are using a single MasterPage layout for our top level and our Area. Now if we do an F5 we can see our updated menu on our home page:

Home Page 2

...and if we navigate to the Latest Articles we can see our content page with the menu from the Articles Area being used:

Latest Articles Page

By creating a Menu.ascx Partial View in the Areas/Articles/Views/Shared directory we can control the content that gets used by the call to Html.RenderPartial() from our MasterPage. The MVC engine will search for a match to the string name passed into the RenderPartial method call within the View directory for the controller being called first, then the Shared view directory for the scope of the Area, followed by the controller name at the top level, and ending with the top level Shared view directory (and apparently in MVC 3 it will repeat for all view engine types that are enabled, or else multiple view engines are enabled by default when creating a new MVC 3 application). We can see this call pattern in the exception thrown if we rename all of our Menu.ascx files to Menu1.ascx, do another F5, and change the url in the browser to navigate to http://localhost:/Articles/Latest.

Missing View Exception

In Part 2 we will take a look at how we can implement the same design using the new Razor view engine. But for now, I think it is time to get some sleep.

Discussion

No comments yet. Be the first!

No new comments are allowed on this post.