Custom Themes in ASP.NET MVC Updated for Preview 5
Update 2009/03/26: There is an updated version of this code (with improvements) that targets the ASP.NET MVC 1.0 RTW located here: /post/2009/03/ASPNET-MVC-Implement-Theme-Folders-using-a-Custom-ViewEngine.aspx
About two weeks ago I posted on how to Implement Custom Theme support in ASP.NET MVC. There were some breaking changes made when the Preview 5 release was released yesterday.
Here’s a short list to a couple of the changes I had to make to my code from the previous post to get it working in ASP.NET MVC Preview 5:
- Delete WebFormThemeViewLocator - The contents of this object is now contained within the ViewEngine itself
- Delete WebFormThemeControllerFactory- This isn't needed anymore.
- Modify WebFormThemeViewEngine - Write a bunch of code that finds the appropriate View to use.
- Modify Global.asax - Remove code that adds the old ControllerFactory, and replace it with code that adds our newly improved WebFormThemeViewEngine
- Modify ControllerBase - Firstly, rename this to ThemeControllerBase since there is not a ControllerBase in System.Web.Mvc. Then, modify the code for the Execute method since it now takes in a RequestContext object as a parameter instead of a ControllerContext object.
Just for reference here’s the code for the WebFormThemeViewEngine.
Download Code: ASPNETMVC_Preview5_CustomThemeImplementation.zip (226.05 kb)
Below is the entire code for the WebFormThemeViewEngine, just for reference. If you are interested in looking at how I implemented this, just download and check out the entire code sample at the link above.
using System; using System.Globalization; using System.Linq; using System.Web.Mvc; public class WebFormThemeViewEngine : System.Web.Mvc.WebFormViewEngine { public WebFormThemeViewEngine() { base.ViewLocationFormats = new string[] { "~/Views/{2}/{1}/{0}.aspx", "~/Views/{2}/{1}/{0}.ascx", "~/Views/{2}/Shared/{0}.aspx", "~/Views/{2}/Shared/{0}.ascx" }; base.MasterLocationFormats = new string[] { "~/Views/{2}/{1}/{0}.master", "~/Views/{2}/Shared/{0}.master" }; base.PartialViewLocationFormats = new string[] { "~/Views/{2}/{1}/{0}.aspx", "~/Views/{2}/{1}/{0}.ascx", "~/Views/{2}/Shared/{0}.aspx", "~/Views/{2}/Shared/{0}.ascx" }; } public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName) { if (controllerContext == null) { throw new ArgumentNullException("controllerContext"); } if (string.IsNullOrEmpty(viewName)) { throw new ArgumentException("Value is required.", "viewName"); } string themeName = this.GetThemeToUse(controllerContext); string[] searchedViewLocations; string[] searchedMasterLocations; string controllerName = controllerContext.RouteData.GetRequiredString("controller"); string viewPath = this.GetPath(this.ViewLocationFormats, viewName, controllerName, themeName, out searchedViewLocations); string masterPath = this.GetPath(this.MasterLocationFormats, viewName, controllerName, themeName, out searchedMasterLocations); if (!(string.IsNullOrEmpty(viewPath)) && (!(masterPath == string.Empty) || string.IsNullOrEmpty(masterName))) { return new ViewEngineResult(this.CreateView(controllerContext, viewPath, masterPath)); } return new ViewEngineResult(searchedViewLocations.Union(searchedMasterLocations)); } public override ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName) { if (controllerContext == null) { throw new ArgumentNullException("controllerContext"); } if (string.IsNullOrEmpty(partialViewName)) { throw new ArgumentException("Value is required.", partialViewName); } string themeName = this.GetThemeToUse(controllerContext); string[] searchedLocations; string controllerName = controllerContext.RouteData.GetRequiredString("controller"); string partialPath = this.GetPath(this.PartialViewLocationFormats, partialViewName, controllerName, themeName, out searchedLocations); if (string.IsNullOrEmpty(partialPath)) { return new ViewEngineResult(searchedLocations); } return new ViewEngineResult(this.CreatePartialView(controllerContext, partialPath)); } private string GetThemeToUse(ControllerContext controllerContext) { string themeName = controllerContext.HttpContext.Items["themeName"] as string; if (themeName == null) themeName = "Default"; return themeName; } private string GetPath(string[] locations, string viewName, string controllerName, string themeName, out string[] searchedLocations) { string path = null; searchedLocations = new string[locations.Length]; for (int i = 0; i < locations.Length; i++) { path = string.Format(CultureInfo.InvariantCulture, locations[i], new object[] { viewName, controllerName, themeName }); if (this.VirtualPathProvider.FileExists(path)) { searchedLocations = new string[0]; return path; } searchedLocations[i] = path; } return null; } }</pre>