Rick Strahl's Weblog  

Wind, waves, code and everything in between...
.NET • C# • Markdown • WPF • All Things Web
Contact   •   Articles   •   Products   •   Support   •   Advertise
Sponsored by:
Markdown Monster - The Markdown Editor for Windows

Themes and Templating Master Pages


:P
On this page:

So I've been mucking around with Themes and Master Pages in ASP.NET 2.0 again. I mentioned that I wasn't quite convinced that Themes are all that useful because Themes natively support only attributes, through CSS files and Skin definitions. Skin files essentially let you pre set default property settings and even templates inside of the Skin definition file.

 

The problem I see with this approach is that it's pretty limited. If you end up building truly customizable sites I think you'll want to do more than muck around with display attributes and possibly rearranging a few templates. Most likely you'd want to be able to completly rearrange the visual layout of a page. The most likely place this happens is probably with a master page and several content place holders inside of it.

 

Unfortunately you can't store Master pages or any executable code in the App_Themes directory.

 

So, after some comments on my previous post I figured that I can still do this relatively easily, but it requires that I take a step outside of the Themes folder. So here's what I came up with is this:

 

  • Define your Themes in APP_Themes as normal
  • Add another folder App_Templates
  • In App_Templates with subfolders for each theme
  • Add the Master Pages or other customizatizable controls in these folders
  • Create a base form class for the application that automatically loads the Master Page

 

After a little playing around I found that this can mostly be automated with some pretty simple code in the form base class so that it's either automatic (if you use a single master page throughout your site) or by setting a single property on the controls.

 

Let's look at what this looks like:

 

 

 

You'll notice that there are two themes – Basic and Simple in App_Themes. I also added a custom App_Templates folder which adds two directories with the same names as the themes and each of those holds a copy of the master page(s) and other templated pages or controls.

 

Each of the .master pages needs to have a unique name (or namespace). They can't be the same since ASP.NET needs to compile both pages regardless of which theme you're using so each master looks like this

 

For Basic:

 

<%@ Master Language="C#" AutoEventWireup="true"

     Codebehind="WebLogMaster.master.cs"

Inherits="Westwind.WebLog.WebLogMaster_Basic"

enableViewState="false" %>

 

For Simple:

 

<%@ Master Language="C#" AutoEventWireup="true"

     Codebehind="WebLogMaster.master.cs"

Inherits="Westwind.WebLog.WebLogMaster_Simple"

enableViewState="false" %>

 

If you don't need separate code for each template you can implement the first page in the Basic folder and then reference the class in the Simple folder, removing the CodeBehind/SourceFile attribute and file:

 

<%@Master Language="C#" AutoEventWireup="true"

Inherits="Westwind.WebLog.WebLogMaster"

enableViewState="false"%>

 

Now you can only change the layout of the template, but have to make sure that all controls that might be used by the MasterPage logic are in place.

 

Note that I'm using Web Application Projects hence the CodeBehind tag as opposed to SourceFile. If you're using standard ASP.NET 2.0 projects use the SourceFile tag instead. Alternately you can also create a common base class and stick in App_Code (or anywhere in WAP) and skip the CodeBehind or SourceFile tags so that the .master file truly is only a template stored in this directory.

 

Actual pages then are implemented inheriting from WebLogBase form:

 

public partial class _Default : WebLogBaseForm

 

It inherits from WebLogBaseForm which handles the logistics of assigning a template master page:

 

public class WebLogBaseForm : wwWebForm, IRequiresSessionState

{

    [Description("Name of the master page in the App_Templates/Theme directory")]

    public string TemplateMasterPage

    {

        get { return _TemplateMasterPage; }

        set { _TemplateMasterPage = value; }

    }

    private string _TemplateMasterPage = "WebLogMaster.master";

 

    protected override void  OnPreInit(EventArgs e)

    {

        // *** Use a templated MasterPage

        if (!string.IsNullOrEmpty(TemplateMasterPage))

            this.MasterPageFile = this.GetThemeTemplatePath(TemplateMasterPage);

       

        base.OnPreInit(e);

    }

 

    /// <summary>

    /// </summary>

    /// <param name="FileName"></param>

    /// <returns></returns>

    public string GetThemeTemplatePath(string Filename)

    {

        if (Filename == null)

            return "~/App_Templates/";

 

        return "~/App_Templates/" + this.Theme + "/" + Filename;

    }

    public string GetThemeTemplatePath()

    {

        return this.GetThemeTemplatePath(null);

    }

}

 

This code overrides the OnPreInit() method which is early enough in the page cycle that you can assign a Master page. The base class implements a TemplateMasterPage property which defaults to the default Master page on the site. The page can change the property in the constructor to use a different Master Page or clear it altogether to not use any Master page.

 

The actual page definition in say Default.aspx looks like this:

 

<%@Page Language="C#" AutoEventWireup="true" Codebehind="Default.aspx.cs"

        Inherits="WebLog._Default"
       
MasterPageFile="~/App_Templates/Basic/WebLogMaster.master" %>

 

Note that the page references the master page in the default theme template directory. This is so that you can see the page with a master in place in designmode. In reality this may not be accurate though if the theme is switched.

 

And that's it.  Now if you switch themes in web.config the master page switches as well.

 

You can store multiple master pages and use this same mechanism. To load another page you can simple do something like this:

 

<%@ Page Language="C#" AutoEventWireup="true" Codebehind="Default.aspx.cs"

Inherits="WebLog._Default"

MasterPageFile="~/App_Templates/Basic/WebLogMaster.master" TemplateMasterPage="AdminMaster.master" %>

 

The TemplateMasterPage can be set in the page definition or in the constructor.

 

I can live with this.

 


The Voices of Reason


 

codeable
March 21, 2006

# re: Themes and Templating Master Pages

Excellent idea - but that doesn't work with nested master pages since there is no "pre-init" to override. Any ideas on that?

Rick Strahl
March 21, 2006

# re: Themes and Templating Master Pages

Wouldn't it work if you reference the nested Master Page as a relative path? So if Master1 lives in App_Templates/Basic/Master1.master and Master2 lived in the same directory, referencing Master2.master directly without the full ~ path reference should work I think.

Haven't tried this though.

Rick Strahl's WebLog
March 26, 2006

# Visual Inheritance with Master Pages and the @Reference tag

Visual Page Inheritance in stock ASP.NET 2.0 projects is a bit of work until you figure out the right syntax. Here's a scenerio I ran into for templating master pages and an example of how to reference another Master page class from another Master page.

Rick Strahl's WebLog
April 12, 2006

# Master Page Inheritance and User Controls

I've run into yet another snag with user controls in ASP.NET 2.0 - this time in relation to Master Pages. Looks like on inherited pages User Controls are treated as separate entities that get pulled into each dynamically compiled page/directory assembly that ASP.NET creates, making user controls difficult to 'inherit'.

# DotNetSlackers: Themes and Templating Master Pages


Rick Strahl's Web Log
October 23, 2006

# Playing around with ASP.NET Themes - Rick Strahl's Web Log

Finally got around to adding themes to a small application. Themes are a great concept and easy to use in ASP.NET 2.0 but they fall a bit short of what would be a commonplace definition of theming for an application.

# smarty, velocity - а в asp,net 2.0 какой аналог?


Jon's Tech Musings
January 16, 2007

# Jon's Tech Musings: Themes and Templating Master Pages - Rick Strahl's WebLog


Dave Barrows
March 07, 2008

# re: Themes and Templating Master Pages

Hi Rick, just came across your interesting article; am searching for a way to use master pages across websites or web applications, and there appear to be only a few ways of doing it; either a) use a virtual directory with IIS; b) use a VirtualPathProvider; c) use some user controls library; or d) some hack using one of the above, like compiling into a DLL, installing it in the GAC, etc. Is there an easier way? I work on an intranet that uses multiple websites. It would be nice to share master pages between apps but ASP.NET appears to make it very difficult. Thanks, -Dave

West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2024