I am a big advocate of the viewmodel and it’s application not only in ASP.NET MVC applications but also in Silverlight, WPF, and even WinForm applications. I always find using the technique of a viewmodel really get’s you thinking about how to structure your entire application. The issue most ASP.NET MVC developers face is the following scenario. We have a site that has recurring functionality on each page; for example, we have a twitter roll which displays all our latest twitters. We would like this to appear on each page, but we don’t want to repeat ourselves in code (usually by copying and pasting code, blech!). So how do we accomplish our goal? An ASP.NET article suggests that you have a base master controller with recurring functionality. I agree with this approach, but I don’t like stuffing objects into ViewData. “Khalid, what is it about ViewData that you are so against?” Let me explain.

ViewData is a string based dictionary, which is dangerous on two levels. From a development standpoint, a dictionary can easily become a dumping ground for data. Before you know it, you have every aspect of your application dependent on values in ViewData. This inherently is bad because to retrieve a value, you need to know the key. This can slow down development for large teams or teams with less than ideal communication. Secondly, accessing a dictionary in the view can lead to really dirty views. I’m sure there are more arguments against and even for ViewData, but as a rule of thumb I try and avoid using this grab bag approach.

Solution

As I mentioned about the referenced ASP.NET article, I agree with the idea of a master controller. It makes sense that recurring functionality would be inherited into new controllers. To put this solution together, let us think about what specifically we need to reach our goal.

  1. We need a base controller with repeating functionality
  2. We need an inheriting controller
  3. We need a viewmodel for the MasterPage
  4. We need a viewmodel for the ViewPage
  5. The MasterPage viewmodel doesn’t need to know about the ViewPage viewmodel

Stating and believing these assumptions gives us a clear path to go down to accomplish our goal of a strongly typed MasterPage/

Addressing Assumptions 1 through 5

We are going to create a base controller that passes a master viewmodel up to the view. In addition to passing the master viewmodel, this base controller has to pass the view’s viewmodel. So we have two models that need to be merged or passed as a pair up to the view. My approach was to create two wrapper classes that will encapsulate the developers viewmodels. I felt this approach would let me reuse this functionality in future projects.

    public class ViewModelForMasterWrapper<TMasterModel>
    {
        public ViewModelForMasterWrapper(TMasterModel masterModel)
        {
            Master = masterModel;
        }

        /// <summary>
        /// Gets or sets the master model.
        /// </summary>
        /// <value>The master model.</value>
        public TMasterModel Master { get; set; }
    }

    public class ViewModelForViewWrapper<TMasterModel, TViewModel>
        : ViewModelForMasterWrapper<TMasterModel>
    {
        public ViewModelForViewWrapper(TMasterModel masterModel, TViewModel viewModel)
            :base(masterModel)
        {
            View = viewModel;
        }

        /// <summary>
        /// Gets or sets the view model.
        /// </summary>
        /// <value>The view model.</value>
        public TViewModel View { get; set; }
    }

You’ll see that the wrapper for the View inherits from the Master wrapper. This is necessary as you’ll see later. Now we need to populate these wrappers with the viewmodels that our application will need. I created a base master controller called BaseMasterController (really creative). The controller will create the ViewModelForViewWrapper and populate the View property and the Master property on the wrapper instance. You’ll see this class below.

   public abstract class BaseMasterController<TMasterViewModel>: Controller
    {
        /// <summary>
        /// Views this instance.
        /// </summary>
        /// <returns></returns>
        protected virtual new ActionResult View()
        {
            return View(ViewData.Model);
        }

        /// <summary>
        /// Views the specified model.
        /// </summary>
        /// <param name="model">The model.</param>
        /// <returns></returns>
        protected virtual new ActionResult View(object model)
        {
            var masterModel = GetMasterViewModel();
            object wrapper = CreateModel(model, masterModel);

            return base.View(wrapper);
        }

        /// <summary>
        /// Gets the master view model.
        /// override this in your master controller.
        /// </summary>
        /// <returns></returns>
        protected virtual TMasterViewModel GetMasterViewModel()
        {
            return default(TMasterViewModel);
        }

        /// <summary>
        /// Creates the model.
        /// </summary>
        /// <param name="model">The model.</param>
        /// <param name="masterModel">The master model.</param>
        /// <returns></returns>
        private static object CreateModel(object model, TMasterViewModel masterModel)
        {
            var modelType = typeof (object);

            if (model != null)
                modelType = model.GetType();

            var types = new[] { typeof(TMasterViewModel), modelType };
            Type generic = typeof (ViewModelForViewWrapper<,>).MakeGenericType(types);

            return Activator.CreateInstance(generic, masterModel, model);
        }
    }

This is an abstract base class. The functionality here is generic so it can be reused in multiple different projects. We now have the majority of our solution, but we need to put it together. To finish this solution we need to create business functionality. We’ll need our own master controller, our own controller, our own master viewmodel, and our own viewmodel. Here are our classes.

    public class HomeIndexViewModel
    {
        public string MessageFromHome { get; set; }
    }

    public class MasterViewModel
    {
        public string MessageFromMaster { get; set; }
    }

    public class MyMasterController : BaseMasterController<MasterViewModel>
    {
        protected override MasterViewModel GetMasterViewModel()
        {
            return new MasterViewModel {MessageFromMaster = "Hello from the master controller."};
        }
    }

    [HandleError]
    public class HomeController : MyMasterController
    {
        public ActionResult Index()
        {
            var model = new HomeIndexViewModel {MessageFromHome = "Hello from the Home controller's Index."};
            return View(model);
        }
    }

After reusing the classes from above, you’ll see that the implementing the actual business functionality is trivial. No more than ten lines (granted this is a simple example). Now, before we can use these we have to strongly type our MasterPage and Views. Let’s take a look.

Typing Our Views

Here you will see why the view wrapper inherits from the master view wrapper. The declarations for the MasterPage and the View will look like the following.

<%@ Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage<ViewModelForMasterWrapper<MasterViewModel>>" %>
<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<ViewModelForViewWrapper<MasterViewModel,HomeIndexViewModel>>" %>

If the ViewModelForViewWrapper class was not an instance of the ViewModelForMasterWrapper class we would get runtime errors when the viewmodel is passed to the view. Now all we have to do is use the models in both our MasterPage and View.

Our MasterPage looks like the following.

<%@ Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage<ViewModelForMasterWrapper<MasterViewModel>>" %>
<%@ Import Namespace="AquaBird.StrongTypeMasterPage.Models" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title><asp:ContentPlaceHolder ID="TitleContent" runat="server" /></title>
    <link href="../../Content/Site.css" rel="stylesheet" type="text/css" />
</head>
<body>
    <div class="page">
        <div id="header">
            <div id="title">
                <h1>Strongly Typed Master Page View</h1>
            </div>
        <div id="main">
            <p id="master"><%=Model.Master.MessageFromMaster %></p>
            <asp:ContentPlaceHolder ID="MainContent" runat="server" />
            <div id="footer">
            </div>
        </div>
    </div>
</body>
</html>

And our View.

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<ViewModelForViewWrapper<MasterViewModel,HomeIndexViewModel>>" %>
<%@ Import Namespace="AquaBird.StrongTypeMasterPage.Models" %>

<asp:Content ID="indexTitle" ContentPlaceHolderID="TitleContent" runat="server">
    Home Page
</asp:Content>

<asp:Content ID="indexContent" ContentPlaceHolderID="MainContent" runat="server">
    <p id="view">
        <%= Model.View.MessageFromHome %>
    </p>
</asp:Content>

After running the solution, my final output looks like this.

Final Result of Strongly Typed Solution

Final Result of Strongly Typed Solution

Conclusion

This solution is only one way of strongly typing your master pages and it surely works well for me. This solution doesn’t fully address the issue of widgetized pages. Complexly composed pages take a lot of time and effort design and are unique to each project. If this solution meets your needs the that is great, but always evaluate solutions to make sure they work for you. Download the solution and see how it will work in your project. Hope this helps you in your ASP.NET MVC project.

Strongly Type MasterPage (271 kb)