Using The Themes Helper In Razor Web Pages

4.94 (16 votes)

Web site skinning is a concept where by a web application user can personalise their experience, by choosing from a selection of prepared themes or skins. Each theme or skin might offer a different design, or content, or both. ASP.NET Web Forms has included a comprehensive Themes framework since version 2.0. WebMatrix developers can find Themes management functionality in the ASP.NET Web Helpers Library package available through the Package Manager. This article looks at how the Themes helper works.

At its most basic, a theme can consist purely of a dedicated CSS style sheet. It may also include a separate layout page, theme-specific sections or partial pages and so on. The Themes helper requires a folder to be designated as the Themes Directory within your site. Each individual theme requires its own folder within that directory. This is illustrated in the following figure which shows a folder named "Themes" (it doesn't have to be named that way, but it makes sense) in which there are four further folders - Blue, Green, Red and White. Each of those folders represent an individual theme. The themes acquires its name from the folder name.

The next image shows these folders expanded to reveal the varying theme-specific sub folders.

Themes are activated through the Themes.Initialize() method. This takes two arguments: a string representing the Themes directory, and the name of the default theme. Taking the structure pictured above as an example, this is how to initialize themes with a default theme of "White":

@{
    Themes.Initialize("~/Themes/", "White");
}

This code is best placed in _AppStart.cshtml, since it executes when the application is first run. Once themes have been intialized, a number of properties and methods become available. Obviously, the ThemeDirectory property will return "Themes", as that has just been set. The CurrentTheme property will return "White" at the moment, as will the DefaultTheme property. The AvailableThemes property returns a ReadOnlyCollection<string>, containing "Blue", "Green", "Red" and "White". This property is populated as a result of iterating the folders in the ThemeDirectory, and grabbing their names. There is one further member of the Themes class - a method called GetResourcePath. There are two overloads for this method: one accepts a string representing the name of a file within the relevant theme folder; and the other accepting a string representing a subfolder within the themes folder, and another string representing the file name. All of these are demonstrated in the sample application which is available for download from the link at the end of the article.

The White theme in the sample application consists purely of a simple layout page in the Layouts folder within the White theme folder:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8" />
        <title>Themes Helper</title>
    </head>
    <body>
        @Helpers.ThemePicker()
        @RenderBody()
    </body>
</html>

The Red and Green themes use exactly the same layout page as eachother except for the style sheet reference. Here's the Red theme layout page:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8" />
        <title>Themes Helper</title>
        <link href="@Themes.GetResourcePath("Styles", "Red.css")" rel="stylesheet" type="text/css" />
    </head>
    <body>
        @Helpers.ThemePicker()
        @RenderBody()
    </body>
</html>

Notice the use of the Themes.GetResourcePath method to locate the theme-specific CSS file in the Styles folder of the Red theme. The Blue layout includes an additional reference to a partial page called _Special.cshtml:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8" />
        <title>Themes Helper</title>
        <link href="@Themes.GetResourcePath("Styles", "Blue.css")" rel="stylesheet" type="text/css" />
    </head>
    <body>
        @Helpers.ThemePicker()
        @RenderPage(Themes.GetResourcePath("Partials", "_Special.cshtml"))
        @RenderBody()
    </body>
</html>

All of these layouts include a method call to a helper called ThemePicker, and this is the mechanism by which users can switch themes. It's just a simple form consisting of a dropdown list:

@helper ThemePicker(){
    var themes = Themes.AvailableThemes.Select(t => new SelectListItem {
        Value = t, 
        Text = t, 
        Selected = t == Themes.CurrentTheme ? true : false
    });
    <div>
        <form method="post" action="">
            Current Theme: @Html.DropDownList("Theme", "Choose Theme", themes) 
            <input type="submit" value="Change theme" />
        </form>
    </div>
}

This helper makes use of the DropDownList HtmlHelper that was discussed in a previous article. LINQ To Objects is used to convert the ReadOnlyCollection of AvailableThemes to a IEnumerable<SelectListItem> for the DropDownList helper. _PageStart.cshtml contains the code that handles the form's submission:

@{
    if(IsPost && !Request["Theme"].IsEmpty()){
        Themes.CurrentTheme = Request["Theme"];
    }
    Layout = Themes.GetResourcePath("Layouts", "_Layout.cshtml");
}

The CurrentTheme is set according to the value that is posted back, and the correct layout page for the current theme is located by the GetResourcePath method. You can see the results of changing the selected theme in the image below.

The source code for the sample application featured in this article is available here.

 

You might also like...

Date Posted:
Last Updated:
Posted by:
Total Views to date: 36413

11 Comments

- Neno

Download link for sample code is broken.

- Mike

@Neno (and others)

Sorry - the link has now been fixed.

- Jason

This is great but how often do websites switch between themes unless they are part of some community driven theme dealio like Google Home page or Yahoo etc or a Browser?

Its great but is it a practical issue to tackle?

- Rafael Zaccanini

Nice... :)

- Mike

@Jason,

It's not just social or community web sites that offer some kind of personalisation mechanisms. Commercial LOB apps are increasingly under pressure to provide means by which users can customise or control their UX.

- Tamean

aaha just what I was looking for... thank you very much! :)

- Gowri Balan

Web site skinning is a good concept .

- J Bishop

Is it possible to add a dynamic value to the _AppStart.cshtml file?

For instance if I want the Theme to initialize with a value from the database?

Thx,
JB

- Mike

@JB

Sure you can query your database in _AppStart and get a value from it. The Initialize method expects two strings. No reason why they have to be literal strings. The values can just as well be variables of type string.

- John

Look at nopCommerce implementation - http://www.nopcommerce.com/. It has more developer friendly theme support

- kawish

Nice:)

Recent Comments

ojorma 17/06/2017 09:24
In response to Razor Pages - The Elevator Pitch
Finally I can say goodbye to webforms...

Mau 06/06/2017 08:58
In response to Razor Pages - The Elevator Pitch
I am missing the razor view code. Thank you for your article. Design pattern should improve the of...

Obinna Okafor 26/05/2017 16:16
In response to Razor Pages - The Elevator Pitch
Thank you for this wonderful piece, Mike. I need more of these...Keep them coming - anything more...

Cyrus 26/05/2017 06:00
In response to Razor Pages - The Elevator Pitch
There are some concern about razor pages performance. Is it faster or slower than MVC? would you a a...

Cyrus 26/05/2017 05:44
In response to Razor Pages - Understanding Handler Methods
well done, thank you....

Satyabrata Mohapatra 23/05/2017 11:41
In response to Razor Pages - Understanding Handler Methods
Nice and easy !! Great post....

Cyrus 16/05/2017 19:55
In response to Razor Pages - Getting Started With The Preview
There is something wrong related to microsoft.dotnetcore.mvc.taghelpers! if you remove it from page...

Cyrus 16/05/2017 10:18
In response to Razor Pages - Getting Started With The Preview
well done mike, it was very useful, I really appreciate that....

Satyabrata Mohapatra 16/05/2017 07:21
In response to Razor Pages - Getting Started With The Preview
Finally!!!! web pages in asp.net core!!! Super excited !!!! Thank u sir for sharing.....Awaiting on...

Daniele 14/03/2017 10:24
In response to Working With Zip Files In ASP.NET MVC
is it possible give to the user a progress bar of the zipping process? Thanks in advance. ...