web 2.0

Dynamic Master Pages in MVC

I have been working on a new project where I wanted to introduce the concepts of themes. This means that end users will be allowed to change the look and feel of the web application from a configuration menu. In order to deliver this functionality I needed to be able to dynamically change the master page on the fly.

My initial response was to use the Page PreInit event. The Page PreInit event is called write before the master page is assigned and therefore makes it a good candidate for dynamically assign the the master page. Unfortunately this is a Page level event. In order to use this solution I would need to override this method on every page. This would be a maintenance nightmare so I immediately eliminated this as a viable solution.

Another solution is to use the View method to assign the master page.

public ActionResult Index()
{
    return View("Index", MyApp.Properties.Settings.Default.Theme);
}

Unfortunately, this solution is also a maintenance nightmare because the change would have to be made in so many places.

Since I am using MVC, I figured that there must be an event somewhere in the controller class that I can override to dynamically assign the master page. After a little bit of reading I discovered the OnActionExecuted event. The OnActionExecuted event passes in a ActionExecutedContext object which can be used to access the ViewResult. The ViewResult class has a MasterName property which can be used to dynamically assign the master page. The code looks like this:

public class HomeController
{
     protected override void OnActionExecuted(ActionExecutedContext filterContext) {
         var action = filterContext.Result as ViewResult;
         if (action != null) {
             action.MasterName = MyApp.Properties.Settings.Default.Theme;
         }  

         base.OnActionExecuted(filterContext);
     }
}

 

Unfortunately, setting the master page in each controller is still not a very elegant solution. I wanted to centralize the code in a single location. Therefore, I decided to create a base class named BaseController that all the other controllers will inherit from.

public class BaseController : Controller
{
     protected override void OnActionExecuted(ActionExecutedContext filterContext) {
         var action = filterContext.Result as ViewResult;
         if (action != null) {
             action.MasterName = MyApp.Properties.Settings.Default.Theme;
         }  

         base.OnActionExecuted(filterContext);
     }
}

Now all my existing controllers can inherit the functionality by making a simple change:

public class PostController : BaseController

This was the perfect solution for my problem because the code is centralized in a single location and it does not require page or view level changes.

blog comments powered by Disqus