find a location for property in a new city

Monday 21 June 2010

MVC pass ViewMasterPage a Model

I have some data that I would like to display on every page on an MVC site. So my PartialView needs to go in the ViewMasterPage with data passed to it, but how is it possible to pass and then access a ViewModel or Model in a ViewMasterPage.

I found quite a cunning way of doing this by using the ViewMasterPage<T> class instead of ViewMasterPage to model and use the data I need.

Solution

I made a new class called BaseViewModel. This will be the class that all ViewModels should now inherit from. The class looks like this:
namespace Web.ViewModels
{
    public class BaseViewModel
    {
        public string MyProperty { get; set; }
    }
}

I now need to make my ViewMasterPage inherit from the generic version of ViewMasterPage%lt;%gt; taking in my new BaseViewModel by making the top line look like this:
<%@ Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage<Web.ViewModels.BaseViewModel>" %>

Now I simply make the ViewModel I was using originally for my View inherit from BaseViewModel and I am ready to start passing data to the ViewMasterPage via MyProperty. The View that used the now modified ViewModel is unaffected and the ViewMasterPage now has access to the new data like so:
<%@ Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage<Web.ViewModels.BaseViewModel>" %>
<%: Model.MyProperty %>

Further

This is all great (and very clever and amazing!) but what about when I am not passing a ViewModel to my View I am just sending a Model? I don't want to have to make a new ViewModel containing this model just for the sake of making it inherit from BaseViewModel.

Well, I made a new class that inherits from BaseViewModel that has a Generic constructor that allows me to pass in my Model. This class looks like this:
namespace Web.ViewModels
{
    public class BaseViewModel : BaseViewModel
    {
        public BaseViewModel() { }
        public BaseViewModel(T innerModel)
        {
            InnerModel = innerModel;
        }

        public T InnerModel { get; set; }
    }
}

I will need to change the type that is passed to the View in the Controller from this:
return View(message);
to this:
return View(new BaseViewModel<Message>(message));

I will then have to change the corresponding View so that it inherits from the new ViewModel and references the InnerModel property instead of just Model:
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/RootMvc.Master" Inherits="System.Web.Mvc.ViewPage<Web.ViewModels.BaseViewModel<Web.Models.Social.Message>>" %>

<asp:Content ID="Content2" ContentPlaceHolderID="RootContent" runat="server">
    <%: Model.InnerModel.Title %>
</asp:Content>

Now your ViewMasterPage is still getting a ViewModel of type BaseViewModel and your main View still has strongly-typed IntelliSense access to your original Model.

Follow britishdev on Twitter

5 comments:

  1. Thanks for this. Am trying to do something and it was really helpful. Do you think it's possible to use an ActionFilter to grab the data and set-up the BaseViewModel?

    Cheers

    Pete

    ReplyDelete
  2. Something like this should work. It's a work in progress though.

    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
    var result = filterContext.Result as ViewResult;

    if (result != null)
    {
    if (result.ViewData.Model != null)
    {
    var model = result.ViewData.Model as BaseViewModel;

    if (model != null)
    {
    model.Title = "Action Title Changed";
    }
    }
    }
    }

    Cheers

    Pete

    ReplyDelete
  3. Thanks Pete, that's a really good idea. Let me know if it works out, it certainly sounds promising

    ReplyDelete
  4. Hey colin,

    It seems to be working out well so far and it's pretty tidy. It also means my controller can focus on the action at hand and not have to worry about loading the common data for the view. Once it's all a but tidier I'll post about it :)

    Cheers, Pete

    ReplyDelete
  5. Hey Colin,

    I just finished writing it up. I'd be interested in hearing your views.

    http://www.big-things.com/2010/11/re-using-elements-in-mvc2-sites/

    Cheers

    Pete

    ReplyDelete