tags:

views:

32

answers:

1

As you can tell from the title, I'm a n00b to MVC. I'm trying to decide what Master to load based on my route configuration settings. Each route has a masterpage property (in addition to the usual url, controller and action properties) and I'm setting the masterpage in the OnPreInit event of a ViewPageBase class (derived from ViewPage). However, I'm not sure if this is the MVC way of doing it? Do I need a controller for this that supplies the masterpage info to the view?

Here's my code snippet.

public class ViewPageBase : ViewPage
{
    protected override void OnPreInit(EventArgs e)
    {
        RouteElement currentRoute = MvcRoutes.GetCurrentRoute();

        //Set master page
        this.MasterPageFile = string.IsNullOrEmpty(currentRoute.MasterPage) ? 
                              MvcConfiguration.DefaultMasterPage : currentRoute.MasterPage;       

        base.OnPreInit(e);
    }

}
+1  A: 

I'm a huge fan of ignoring anything that seems webformish and trying to always find the right MVC hook. In this case creating a custom view engine is the correct extensibility hook for this. If you think about it the engine that decides what .aspx file to render should also decide what mater page that aspx file uses. Here is some semi-psuedo ( I've never compiled it ) code that should work.

public class DynamicMasterViewEngine: VirtualPathProviderViewEngine 
{
public DynamicMasterViewEngine()
{                
 /* {0} = view name or master page name 
  * {1} = controller name      */  

 MasterLocationFormats = new[] {  
     "~/Views/Shared/{0}.master"  
 };  

 ViewLocationFormats = new[] {  
     "~/Views/{1}/{0}.aspx",  
     "~/Views/Shared/{0}.aspx"
 };  

 PartialViewLocationFormats = new[] {  
     "~/Views/{1}/{0}.ascx",               
    }; 
}

protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
{
    throw new NotImplementedException();
}

protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
{

    return new WebFormView(viewPath, masterPath );
}

public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
{
    RouteElement currentRoute = MvcRoutes.GetCurrentRoute();

    var masterName = string.IsNullOrEmpty(currentRoute.MasterPage) ? 
                          MvcConfiguration.DefaultMasterPage : currentRoute.MasterPage;       

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

protected override bool FileExists(ControllerContext controllerContext, string virtualPath)
{
    return base.FileExists(controllerContext, virtualPath);
}        
}

ported from this answer

jfar
Hey jfar,I was able to create a custom view engine and wire up my master page logic using your suggested method. Works like a charm. Many thanks. I have another question.I have also added a view-loading mechanism to the FindView() method that essentially finds the full view path based on the current route. It works great too. What's driving me crazy though is the FindView() method is being called twice. Is this normal for every request?
Praveen
I found the answer to my previous comment. This has to do with the way ViewEngineCollection performs it's search to find the right View. The first time around, it tries to find a cached version of the View with the useCache flag set to true. Then, it makes a second iteration with useCache=false.
Praveen