A Different Approach To Master Pages

I’ll just say it out loud , I don’t understand why do we ( or at least the MVC ) use _ViewStart to apply master pages. I just don’t get it. I mean we have a controller layer for decisions right? Why would I want to put decision logic in View layer ( or folder ).

I’m not a MVC purist , not eve close but still I don’t like this _ViewStart thing either.

@{ //Yeap it's Razor
//And we don't want any Master Page if it's an Ajax Request right?
Layout = Request.IsAjaxRequest() ? null : "~/Views/Shared/_Layout.cshtml";
}

Another solution I learned a while ago was putting decision logic into ViewEngine. In TekPub Mastering Mvc Series , [Rob Connery](http://twitter.com/robconery) derived a personal View Engine, from WebForms view engine and put master page decision logic into that . Sounds like a nice idea isn’t it?

   

public override ViewEngineResult FindView(ControllerContext controller , string viewName, string masterName, bool useCache)
{

masterName = "DefaultMasterPage";

if(controllerContext.HttpContext.User.IsInRole("Administrator")
{
//Admin gets a special Master Page
masterName = "AdminMasterPage";
}

return base.FindView(controllerContext, viewName, masterName, useCache);
}

I like that a lot more than _ViewStart but still , adding a new View Engine and putting master page decision making process in a View Engine? Not a fan of that. Looks nice , works nice but I really don’t want to go to ViewEngine files whenever I want to change Master Page logic , feels kinda weird and out of place.

Of course , yet another and probably simplest option is to set Master Page in Controller Action while returning a view

   

public ActionResult About()
{
    return View("About","_SpecialLayout");
}

Well it’s in Controller now , right? But do we want to type the master page name in every single action? Of course not! Then we have to find a way to centralize this decision in Controller , do it once and then forget all about it right?

Just like what we did before ( in Fascho Controller post ) , we can just override OnResultExecuting method and put Master Page decision logic there! And since we’ll derive all our Controllers from this new Controller base class , it’ll be centralized , easy and pretty.

   

protected override void OnResultExecuting(ResultExecutingContext filterContext)
{
    if (filterContext.Result is ViewResult) //if we're done and returning a view 
    {
        var temp = (ViewResult)filterContext.Result;

        if (!IsAjax) //We dont want a masterpage around data if it's an Ajax call
        if (string.IsNullOrEmpty(temp.MasterName)) // And we don't want to override if there already is a masterpage 
        {
            //MasterPage selection logic
            temp.MasterName = IsAdmin() ? "_AdminLayout" : "_Layout";
        } 
    }
    base.OnResultExecuting(filterContext);
}

Doesn’t it look good? OnResultExecuting runs right after your Action returns something ( and obviously before OnResultExecuted ) so it’s not much more different than sending MasterPage name as a parameter , yet it’s centralized and all actually inside the Controller layer. 

You can find a sample solution file below. Fascho Controller now includes both this and previous Ajax solution but I did my best to seperate them with comments so it shouldn’t be a problem.

Visual Studio 2010 Solution

Leave a Reply