Setting An ASP.NET Theme in the PreInit Event Handler

Monday, March 13, 2006

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.


Comments
Rick Strahl Monday, March 13, 2006
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.

scott Monday, March 13, 2006
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!
Jim Homminga Sunday, March 26, 2006
thanks man, worked perfect, your site guides well. not only what to do, but why, when and where. many, many thanks,
Jimh
Harry Tuesday, April 4, 2006
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
scott Wednesday, April 5, 2006
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
Bill Wednesday, May 3, 2006
You solved my problem, thanks, great work.
Access using MAsterpages needs to be rethought by MS.
Abhishek Friday, May 19, 2006
hi scott,

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

Cheerio...
Claudiu Thursday, September 28, 2006
Hi Harry,
Here is my solution for applying theme programatically to the whole website: www.4invent.com. 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
Ron Reuter Monday, November 6, 2006
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";
}
gravatar george baxter Monday, February 14, 2011
Thank you, i was using a code like this until now.

Request.Form["ctl00$PlaceHolderMain$DropDLLanguage"]

really helpful.
Comments are now closed.
by K. Scott Allen K.Scott Allen
My Pluralsight Courses
The Podcast!