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

Dynamic Themes assignment


:P
On this page:

 

If you’ve been following this Blog you probably know I’ve been mucking around with a bunch of infrastructure stuff recently and one of the things I’ve played with a lot is trying to build a generic solution for skinnable UIs. To that effect I ended up using a mixture of Themes and Master Pages in a separate set of template directories that match the Themes templates.

 

The idea if you change the Theme the Master Page template changes along with the theme. I rigged this up a while ago but it was a bitch to get this to work right in ASP.NET 2.0 stock projects because of various inheritance issues. In the end I got it to work though although I had to make some compromises in what goes onto the master pages (it works fine in WAP though).

 

Today I finally put that stuff live on my site, and just for kicks I decided to try and switch themes on the pre-compiled site. To my dismay – nothing happened. I changed the theme in the web.config file, but it no effect at all on the site... What? Isn’t that the whole point of Themes?

 

Back here in my dev environment it works fine, so it must have something to do with precompilation. I double checked by compiling my app into a local deployment directory and then setting up a virtual for it and sure enough I see the same behavior – the app is stuck with whatever Theme was used in web.config when the project was compiled and changing the value in Web.config has zero effect. Chalk that one up to another unexpected ASP.NET 2.0 project behavior where design time and runtime vary.

 

This project is a stock project compiled with fixed file options, non-updateable and then set up using ASPNET_MERGE into a single assembly. What’s really odd is that the web.config setting that is during compile time seems to determine which Theme the app is stuck on.  I haven’t had the inclination to check this out further, but this seems pretty silly if theming adjustments through web.config doesn’t work with a precompiled site somehow. I must be missing something here...

 

I didn’t bother digging deeper because I wanted to add dynamic application driven theming to the site anyway, and so the web.config setting was just a temporary solution. In the end I wanted it so that Themes can be interactively changed through the Configuration API of the application. Thankfully setting the Theme through Page.OnPreInit works correctly and that's what I'm doing now.

 

In order to select set the theme (which sets both the Theme as well as the Master Page template) at runtime I use code like this:

 

protected override void OnPreInit(EventArgs e)

{

    this.Theme = App.Configuration.Theme;

 

    // *** Use a templated MasterPage

    if (this.TemplateMasterPage != "" && !string.IsNullOrEmpty(this.Theme))

        this.MasterPageFile = this.GetThemeTemplatePath(this.TemplateMasterPage);

 

    base.OnPreInit(e);

}

 

/// <summary>

/// Figures out the appropriate theme specific Template path

/// that is used to hold Master Pages and other templates.

///

/// This version returns a fully qualified file Url.

/// </summary>

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

/// <returns></returns>

public string GetThemeTemplatePath(string Filename)

{

    if (Filename == null)

        return "~/App_Templates/";

 

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

}

 

This code is defined in my page base class for the app. It first gets the Active Theme for the application from my Configuration store and then assigns that explicitly which effective changes the theme. The Theme is then used to look up the appropriate Master Page template by filename. Nice and simple and it works effectively.

 

In the configuration form the Theme is then displayed and selected and stored in the global configuration object. To get a list of themes I found this code from K. Scott Allen.

 

/// <summary>

/// Loads the list of available themes into txtTheme dropdown

/// </summary>

private void LoadThemes()

{

    VirtualDirectory ThemeDir = HostingEnvironment.VirtualPathProvider.GetDirectory("~/App_Themes");

 

    foreach (VirtualDirectory Dir in ThemeDir.Directories)

    {

        this.txtTheme.Items.Add( Dir.Name );

    }

  

}    

 

I had been parsing the directories out of the App_Themes directory and like Scott I was worried about the permissions issues that might not allow access there.

 

The form displays among other things the list of themes and lets the admin pick the theme that is used on the site.

 

I’ve got what I needed working but I’m still thinking about this funky behavior where the web.config setting isn’t changing the them... Any body have any thoughts?

 


The Voices of Reason


 

Scott
June 27, 2006

# re: Dynamic Themes assignment

Yes, I've run into this also (http://odetocode.com/Blogs/scott/archive/2006/02/24/3019.aspx).

I had to use an HttpModule to read web.config and override the settings :/

Greg
June 27, 2006

# re: Dynamic Themes assignment

Rick,
Your blog comes up for just about any asp.net query I run on Google. They are always excellent posts. Nice work!

Rick Strahl
June 27, 2006

# re: Dynamic Themes assignment

Scott, thanks. I spend a little bit og time looking around trying to figure out where this is getting set. I can't find it anywhere in the generated assembly code for the site. The themes classes hard code their paths, but these

It seems really un-necessary that this is hardcoded. Rather than reading a dynamic value...

Rick Strahl
June 27, 2006

# re: Dynamic Themes assignment

BTW, the master page can also be set globally in an HTTP Module, HttpApplication Global handler. You can hook HttpApplication::PreRequestHandlerExecute() and in that method pick up the handler and hook it's PreInit event:

void context_PreRequestHandlerExecute(object sender, EventArgs e)
{
Page page = HttpContext.Current.CurrentHandler as Page;
if (page != null)
{
page.PreInit +=new EventHandler(page_PreInit);
}
}

And yes, this one's also courtesy of Scott's site from this excellent post on Master Pages which is releated to this topic:

http://www.odetocode.com/Articles/450.aspx

Sean O'Connor
June 27, 2006

# re: Dynamic Themes assignment

Before setting the page theme in an HttpModule, be sure page.EnableTheming is true.

Scott
June 27, 2006

# re: Dynamic Themes assignment

Rick, did you find it?

Pre-compile a non-updateable web site with the theme set in web.config.

Then look at the BuildControlTree for your webforms. They will look like:

private void __BuildControlTree(default_aspx __ctrl)
{
__ctrl.Theme = "default";
this.InitializeCulture();
IParserAccessor accessor1 = __ctrl;
// ...
}

Rick Strahl
June 27, 2006

# re: Dynamic Themes assignment

Scott, thanks. Found it now.

The code is always generated with a hardcoded value, regardless of whether it's updatable or not it looks like. The reason it works in design mode is that VS forces the AppDomain to cycle and it apparently recompiles the project.

But even in a non-pre-compiled project __BuildControlTree() has the hardcoded theme in it.

Well, live and learn <g>. I guess we have to manage this on our own.

Rick Strahl
June 27, 2006

# re: Dynamic Themes assignment

Sean... actually I think ASP.NET doesn't respect EnableTheming on the Page either. If I turn off enable theming on the page I still get the theme specified in Web.config. The only way to not get it is to set Theme="" it seems.

Onion Blog
August 02, 2006

# Setting StyleSheetTheme globally - impossible?

# DotNetSlackers: Dynamic Themes assignment


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