Custom Themes in ASP.NET MVC Updated for Preview 5
Aug 29, 2008 • Chris Pietschmann • ASP.NET
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>
Chris Pietschmann
DevOps & AI Architect | Microsoft MVP | HashiCorp Ambassador | MCT | Developer | Author
I am a DevOps & AI Architect, developer, trainer and author. I have nearly 25 years of experience in the Software Development industry that includes working as a Consultant and Trainer in a wide array of different industries.




