Setting An ASP.NET Theme in the PreInit Event Handler

Let’s say I want the user to select their favorite theme for my application. I can make a list of available themes in a DropDownList control.

<asp:DropDownList ID="_themeList"
                  
runat="server"
                  
AutoPostBack="True">
  <asp:ListItem>Default</asp:ListItem>
  <asp:ListItem>Odeish</asp:ListItem>
  <asp:ListItem>Codeish</asp:ListItem>
</
asp:DropDownList>

I know I must set the Theme property before or during the page’s PreInit event.

What’s wrong with the following code?

protected void Page_PreInit(object sender, EventArgs e)
{
    
if (IsPostBack)
    {
        Theme = _themeList.SelectedValue;
    }
}

The above code throws a null reference exception. PreInit fires before the page instantiates its controls, so _themeList is null (Nothing). I can’t use the _themeList control, but I can go directly to the Request.Form collection. The DropDownList (an HTML select) will post its new value into the form collection.

What could go wrong with this code?

protected void Page_PreInit(object sender, EventArgs e)
{
    
if (IsPostBack)
    {
        
if (Request[_themeListID] != null)
        {
            Theme = Request[_themeListID];
        }
    }
}

const string _themeListID = "themeListID";

Hint: this code will work for many webforms, but not if you are using a master page.

The problem with asking for “_themeList” is that “_themeList” is a server side ID. The browser will submit the form with a unique client side ID. If the DropDownList ends up inside an INamingContainer, the UniqueID property is not the same as the ID property (see my FindControl article for more on INamingContainer). If the DropDownList is on a master page, the UniqueID might look like “ctl00$_themeList”. If the DropDownList is inside a ContentPlaceHolder control, the UniqueID might look like "ctl00$ContentPlaceHolder1$_themeList" .

There are many ways to solve the problem. One solution might be a brute force search of the form collection for a key ending with “_themeList”. Another approach is to stash the list's UniqueID into a hidden form field.

protected void Page_PreInit(object sender, EventArgs e)
{
    
if (IsPostBack)
    {
        
string uniqueID = Request[_themeListIDKey];

        
if (uniqueID != null && Request[uniqueID] != null)
        {
            Theme = Request[uniqueID];
        }
    }
}

protected void Page_Load(object sender, EventArgs e)
{
    ClientScript.RegisterHiddenField(
            _themeListIDKey, _themeList.UniqueID
        );
}

const string _themeListIDKey = "_themeListIDKey";

I’m sure you can think of some other elegant ways to solve the problem. The trick, as always, is finding out what the problem really is.

posted on Sunday, March 12, 2006 9:33 PM by scott

Comments

Monday, March 13, 2006 2:46 AM by Rick Strahl

# re: Setting An ASP.NET Theme in the PreInit Event Handler

Scott,

That one's bitten me more than a few times as well and the first couple of times (I can be a slow learner) it took me a while to figure out that the MasterPage was changing the containership :-}...

The problem in PreInit is that the the list isn't set yet and SelectedValue won't have its value set so it'll certainly be null.

Monday, March 13, 2006 4:33 AM by scott

# re: Setting An ASP.NET Theme in the PreInit Event Handler

Yes, Rick that's it exactly. I didn't explicitly mention it in my last solution. I can't use _themeList.UniqueID because _themeList is null during PreInit, and even if it wasn't null, the SelectedValue wouldn't be set yet!
Monday, March 13, 2006 7:09 PM by Christopher Steen

# Link Listing - March 13, 2006

All in all we’re just another brick in the
wall… [Via: rocky@lhotka.net (Rockford
Lhotka) ]
ASP.NET...
Saturday, March 25, 2006 7:12 PM by Jim Homminga

# re: Setting An ASP.NET Theme in the PreInit Event Handler

thanks man, worked perfect, your site guides well. not only what to do, but why, when and where. many, many thanks,
Jimh
Tuesday, April 04, 2006 8:56 AM by Harry

# User choice changing the whole Application's Theme

Hi, Scott.

Great article. One thing I cannot find and don't think is possible is that everything points to the fact that you can only change a PAGE'S theme, but it is NOT possible to change the APPLICATION'S theme programmatically via a user's input. So, in your example, you cannot have a user select from the dropdownlist a theme that will carry over to all the pages in the application. I realize that you can change the application's theme by hard-coding in the web.config. Is this a limitation with using themes in ASP.NET 2.0?

Thanks in advance,
Harry
Tuesday, April 04, 2006 10:34 PM by scott

# re: Setting An ASP.NET Theme in the PreInit Event Handler

Harry: There is a configuration API now that let's you programatically write to the web.config file easily. It requires an app restart unless you push the 'writeable' config section into an external file. This article has some examples: http://www.odetocode.com/Articles/418.aspx
Wednesday, May 03, 2006 6:19 AM by Bill

# re: Setting An ASP.NET Theme in the PreInit Event Handler

You solved my problem, thanks, great work.
Access using MAsterpages needs to be rethought by MS.
Friday, May 19, 2006 9:02 AM by Abhishek

# re: Setting An ASP.NET Theme in the PreInit Event Handler

hi scott,

Article helped me alot....thanks...

Cheerio...
Thursday, September 28, 2006 2:54 PM by Claudiu

# re: Setting An ASP.NET Theme in the PreInit Event Handler

Hi Harry,
Here is my solution for applying theme programatically to the whole website: http://www.4invent.com/post65-Apply-theme-programmatically-to-the-whole-website-in-asp.net-2.0.aspx. Actually the method is used in the blog where the post is published, every user will have the ability to have it's own template selected from a list or to build a css file by itself. Let me know if you need assistance.

Claudiu
Monday, November 06, 2006 12:39 PM by Ron Reuter

# re: Setting An ASP.NET Theme in the PreInit Event Handler

I decided to use postback-enabled radio buttons for my theme selection. In Page_PreInit(), I then just check the value of __EVENTTARGET, which will be the radio button that the user just clicked on, like so:

String eventTarget = Request.Form["__EVENTTARGET"];

if (eventTarget == "Radio_Soy")
{
Theme = "Soy";
}
else if (eventTarget == "Radio_Mocha")
{Theme = "Mocha";
}