views:

43

answers:

2

I'd like to set a default Razor layout via code in either a base controller or an attribute. It was mentioned in the Docs that this is possible, but I can't figure out how it's done.

I know there's the masterPage parameter to the View method available, but I would like all views returned by a controller to have this value set automatically.

And no, I can't use _ViewStart for this since my views are going to be in various places (this is not a normal MVC site configuration).

Thanks

A: 

The easiest way I can think of doing this is by having your controllers derive from a custom base class that overrides the View method:

public class MyControllerBase : Controller {
    public override ViewResult View(string viewName, string masterName, object model) {
        if(String.IsNullOrEmpty(masterName)) {
            masterName = GetDefaultLayout();
        }
        base.View(viewName, masterName, model);
    }

    public virtual string GetDefaultLayout() {
        return // your default layout here
    }
}

In the code above you could explicitly set the masterName to some hardcoded value. Or your Controllers could override the method to provide a Controller-specific layout. Or you could read it from some attribute on the Controller, something similiar to:

masterName = GetType().GetCustomAttributes().
             OfType<MyCustomAttribute>().FirstOrDefault().DefaultLayoutPage;

Of course you'd have to create your MyCustomAttribute.

marcind
I like the base controller idea here, it seems the most straightforward. Do you know if there is any way for an attribute to intercept the call to View?
Brian Vallelunga
I'm not sure what you mean, but attributes can't do anything on their own. They carry information (or maybe behavior) that has to be invoked by something that has context (like the ControllerActionInvoker). What exactly do you mean by "intercept"?
marcind
Well, if I built an action filter attribute that executed before the view is executed, I could modify the view and set the layout page. I'm just not sure if it's possible to modify the ActionResult like that, although I suspect it is.
Brian Vallelunga
Action filters are executed before views are even created so you wouldn't be able to set the layout anywhere.
marcind
A: 

I think you could just write an ActionFilter like...

public class YourCustomLayoutAttribute : ActionFilter, IResultFilter
{
       public void OnResultExecuting(ActionExecutedContext 
           filterContext)
       {
           var viewResult = filterContext.Result as ViewResult;
           if(viewResult != null)
           {
              // switch the layout
              // I assume Razor will follow convention and take the "MasterName" property and change the layout based on that.
              viewResult.MasterName = "CustomLayout";
           }
       }
}

I just wrote this code by the seat of my pants with no compiler so it probably won't compile but you probably get the idea. I think IResultFilter is the correct interface you want, it has methods that execute right before the view is rendered. If this is correct, you should be able to modify the MasterName for the view that is about to be rendered on the fly.

This would be the controller code usage.

[YourCustomLayout] // this should trigger your custom action result for all actions
public class MyController : Controller
{
   public ActionResult Index()
   {
      return View("Index", "MainLayout"); // even if you were to use the overload to set a master, the action result should override it as it executes later in the pipeline.
   }
}
Jab