Click here to Skip to main content
15,867,453 members
Articles / Web Development / ASP.NET

ASP.NET MVC Dynamic Themes

Rate me:
Please Sign up or sign in to vote.
4.36/5 (7 votes)
26 Jan 2009CPOL 106.1K   2.9K   41   14
Implement Theme selection in ASP.NET MVC.

Introduction

I really needed to enable themes for my application, and I found an interesting article about it by Chris Pietschmann. In my point of view, the only problem with his approach is that you need to repeat each page for all themes. Well, I only want to create one page per theme, and have a master page and CSS files for each theme.

Using the code

For this project, I made some assumptions:

  • The master page for each theme has the name Site.Master.
  • The master page file and CSS files will be placed on a folder with the name of the theme.
  • All the theme folders will be placed in the Content folder.

The file structure will be like this:

Now, let’s write a custom View Engine that will inherit from WebFormViewEngine. This code is only a sketch of what I want to implement. For instance, I want to save the user theme selection so that, in the next login, the user has his option set.

C#
public class ThemeViewEngine : WebFormViewEngine
{
    public ThemeViewEngine()
    {
        base.ViewLocationFormats = new string[] {
            "~/Views/{1}/{0}.aspx",
            "~/Views/{1}/{0}.ascx",
            "~/Views/Shared/{0}.aspx",
            "~/Views/Shared/{0}.ascx"
        };

        base.MasterLocationFormats = new string[] {
            "~/Content/{2}/Site.master"
        };

        base.PartialViewLocationFormats = new string[] {
            "~/Views/{1}/{0}.aspx",
            "~/Views/{1}/{0}.ascx",
            "~/Views/Shared/{0}.aspx",
            "~/Views/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.GetViewPath(this.ViewLocationFormats, viewName, 
                          controllerName, out searchedViewLocations);
        string masterPath = this.GetMasterPath(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)), this);
        }
        return new ViewEngineResult(
          searchedViewLocations.Union<string>(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.GetViewPath(this.PartialViewLocationFormats, 
                             partialViewName, controllerName, out searchedLocations);

        if (string.IsNullOrEmpty(partialPath))
        {
            return new ViewEngineResult(searchedLocations);
        }
        return new ViewEngineResult(this.CreatePartialView(controllerContext, 
                                    partialPath), this);
    }

    private string GetThemeToUse(ControllerContext controllerContext)
    {
        if (controllerContext.HttpContext.Request.QueryString.AllKeys.Contains("theme"))
        {
            string themeName = controllerContext.HttpContext.Request.QueryString["theme"];
            controllerContext.HttpContext.Session.Add("Theme", themeName);
        }
        else if (controllerContext.HttpContext.Session["Theme"] == null)
        {
            controllerContext.HttpContext.Session.Add("Theme", "Default");
        }
        return controllerContext.HttpContext.Session["Theme"].ToString();
    }

    private string GetViewPath(string[] locations, string viewName, 
                   string controllerName, 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 });
            if (this.VirtualPathProvider.FileExists(path))
            {
                searchedLocations = new string[0];
                return path;
            }
            searchedLocations[i] = path;
        }
        return null;
    }

    private string GetMasterPath(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;
    }
}

To finish this sample, just change the global.asax, removing all engines from the View Engine and adding the custom one:

C#
protected void Application_Start()
{
    RegisterRoutes(RouteTable.Routes);

    ViewEngines.Engines.Clear();
    ViewEngines.Engines.Add(new ThemeViewEngine());
}

Well, hope that’s useful.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Portugal Portugal
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
Questionsolution that i found for more than one color theme Pin
RaunakGupta17-Nov-16 19:06
RaunakGupta17-Nov-16 19:06 
GeneralAreas in MVC 2 Pin
Yusree Allie21-Oct-10 1:27
Yusree Allie21-Oct-10 1:27 
GeneralThank a lot! Pin
Nguyen Tien Lam12-Sep-10 4:05
Nguyen Tien Lam12-Sep-10 4:05 
GeneralIt's not work Pin
Nguyen Tien Lam12-Sep-10 1:42
Nguyen Tien Lam12-Sep-10 1:42 
GeneralRe: It's not work Pin
Nguyen Tien Lam12-Sep-10 1:44
Nguyen Tien Lam12-Sep-10 1:44 
GeneralSmall change needed to make it work in MVC2 Pin
keeeeeeeeeeiff11-Aug-10 5:50
keeeeeeeeeeiff11-Aug-10 5:50 
QuestionWhat to do if I have more than one Master page? Pin
prasadvemala31-Oct-09 1:44
prasadvemala31-Oct-09 1:44 
GeneralASP.NET MVC Dynamic Themes problem solved PinPopular
totallyweb8-May-09 15:40
totallyweb8-May-09 15:40 
GeneralRe: ASP.NET MVC Dynamic Themes problem solved Pin
wex6923-Sep-09 3:58
wex6923-Sep-09 3:58 
QuestionASP.NET MVC Dynamic Themes Pin
totallyweb8-May-09 15:24
totallyweb8-May-09 15:24 
GeneralMVC RC1 Pin
bidulle3-Feb-09 0:24
bidulle3-Feb-09 0:24 
QuestionOr use .NET 'App_Themes' folder? Pin
Abi Bellamkonda2-Feb-09 15:32
Abi Bellamkonda2-Feb-09 15:32 
AnswerRe: Or use .NET 'App_Themes' folder? Pin
dbo.net10-Apr-09 14:32
dbo.net10-Apr-09 14:32 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.