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