Stephen Walther
Superexpert
October 2004
Updated March 2005
Applies to:
Microsoft ASP.NET 2.0
Microsoft Visual Studio 2005
Summary: Stephen Walther discusses the ins and outs of themes, a feature new to ASP.NET 2.0, including how to use both cascading style sheets and images with themes, and how you can dynamically load themes at run time. (20 printed pages)
Contents
Introduction
Creating and Applying a Simple Theme
Default Skins vs. Named Skins
Applying a Theme to an Entire Application
Themes vs. StyleSheetThemes
Images and Themes
Cascading Style Sheets and Themes
Dynamically Loading Themes
Conclusion
Related Books
Introduction
Themes, a new feature of Microsoft ASP.NET 2.0, enable you to define the appearance of a set of controls once and apply the appearance to your entire Web application. For example, by taking advantage of themes, you can define a common appearance for all of the TextBox controls in your application, such as the background and foreground color, in one central location. Themes enable you to easily create and maintain a consistent look throughout your Web site.
Themes are not the same thing as cascading style sheets. Cascading style sheets enable you to control the appearance of HTML tags on the browser. Themes, on the other hand, are applied on the server and they apply to the properties of ASP.NET controls. For example, you can use a theme, but not a cascading style sheet, to specify whether or not a GridView control displays a header or footer.
Themes are not the same thing as master pages. Master pages, another new feature of ASP.NET 2.0, enable you to specify a common layout for multiple content pages in a Web application. Themes, but not master pages, enable you to control the appearance of individual controls within a page (You can and probably will use both master pages and themes at the same time when designing a Web application).
In this article, you learn how to take advantage of themes in your ASP.NET 2.0 applications. You will learn how to use both cascading style sheets and images with themes. We will also examine how you can dynamically load themes at run time.
Creating and Applying a Simple Theme
ASP.NET 2.0 does not ship with any default themes. When ASP.NET 2.0 is released, you will be able to download and use themes from Web sites such as the www.ASP.net Web site. For now, however, you must create all of your own themes from scratch.
Furthermore, you should be warned, Microsoft Visual Web Developer does not provide any tool support for creating themes. You certainly can create themes and apply themes to Web pages by using Visual Web Developer. However, you won't see the effect of applying a theme to a page until you actually open the page in a Web Browser. In other words, Visual Web Developer does not provide any designer support for creating themes.
With these warnings out of the way, let's go ahead and create a simple theme. The first step required for creating a theme is to create a new folder named App_Themes in the root of your application. The ASP.NET Framework automatically looks for this folder when you attempt to apply a theme.
After you create the App_Themes folder, you can create one or more themes by adding subfolders to the App_Themes folder. The first theme that we will create is named the OrangeTheme. Therefore, we need to add a new folder to the App_Themes folder named OrangeTheme.
Next, we need to add a Skin file to the OrangeTheme folder. The Skin file is the file that actually contains the format settings applied by a theme. In other words, the file contains one or more control skins applied by the theme. You can name a Skin file anything you want as long as it ends with the extension .Skin.
A theme might contain one Skin file or hundreds of Skin files. You can name the Skin files anything you want. It really doesn't matter since the ASP.NET Framework merges all of the Skin files together and treats the files as a single Skin file when applying a theme to a page.
We will use the Skin file in Listing 1 (named FormControls.Skin) for our OrangeTheme:
Listing 1. FormControls.Skin
<asp:TextBox
BackColor="Orange"
ForeColor="DarkGreen"
Runat="Server" />
<asp:Button
BackColor="Orange"
ForeColor="DarkGreen"
Font-Bold="True"
Runat="Server" />
You should notice that the Skin file in Listing 1 contains the declarations for a TextBox and Button control. The BackColor and ForeColor properties are provided with values for both controls. In addition, the Button control is declared to have a bold font.
Notice that you specify the appearance of a control with a Skin file by declaring an instance of the control and setting one or more control properties. There are limitations on the control properties you can set in a Skin file. In general, you are limited to setting appearance properties. For example, you can set properties of a TextBox control such as its BackColor, ForeColor and even its Text property. However, you cannot set the TextBox control's AutoPostBack property in a Skin file.
Furthermore, you cannot use skins with every ASP.NET control. For example, you cannot use skins with the Repeater control, the Literal control, or the LoginView control. You also cannot use skins with User Controls (however, a skin can apply to the controls contained in a User control).
When the FormControls skin is applied to a page, every TextBox and Button control in the page will appear with the property values specified in the Skin file. This is true even in the case when a TextBox control in the page already has a value specified for its BackColor property. The Skin file overrides control properties in a page.
The page in Listing 2 has the OrangeTheme applied to it:
Listing 2. OrangePage.aspx
<%@ Page Theme="OrangeTheme" %>
<html>
<head runat="server">
<title>Orange Page</title>
</head>
<body>
<form id="form1" runat="server">
Enter your name:
<br />
<asp:TextBox
ID="txtName"
Runat="Server" />
<br /><br />
<asp:Button
ID="btnSubmit"
Text="Submit Name"
Runat="Server" />
</form>
</body>
</html>
The page in Listing 2 contains a form that asks for the user's name. There is nothing special about this page except for the addition of the theme directive at the top of the page. The theme directive is used to apply the OrangeTheme theme to the page.
When you open the page in Listing 2 in a Web browser, the TextBox and Button controls appear with the format settings declared in the Skin file (see Figure 1).
.gif)
Figure 1. Applying a simple theme to an ASP.NET page
A Skin file can contain more complicated format settings than those contained in Listing 1. For example, the GridView.Skin file in Listing 3 contains a skin for a GridView control that can be used when displaying the contents of the Products database table included in the sample Microsoft SQL Server Northwind database. (The GridView control replaces the DataGrid control in the ASP.NET 2.0 Framework.)
Listing 3. GridView.Skin
<asp:GridView
AutoGenerateColumns="false"
CellPadding="5"
Font-Name="Arial"
Runat="Server">
<AlternatingRowStyle BackColor="LightBlue" />
<Columns>
<asp:BoundField
HeaderText="Product Name"
DataField="ProductName" />
<asp:BoundField
HeaderText="Price"
DataField="UnitPrice"
DataFormatString="{0:c}" />
</Columns>
</asp:GridView>
The Skin file in Listing 3 contains a GridView control with two columns (see Figure 2). Notice that the second column includes a DataFormatString which is used to format the UnitPrice database column as a currency amount.
.gif)
Figure 2. A complicated GridView skin
You can use the GridView.Skin skin with the page in Listing 4 (assuming that you have added the skin in Listing 3 to the OrangeTheme folder).
Listing 4. SkinnedGridView.aspx
<%@ Page Theme="OrangeTheme" %>
<html>
<head runat="server">
<title>Skinned GridView</title>
</head>
<body>
<form id="form1" runat="server">
<asp:GridView
ID="GridView1"
DataSourceID="ProductSource"
Runat="Server" />
<asp:SqlDataSource
ID="ProductSource"
ConnectionString="Server=localhost;Trusted_Connection=true;Database=Northwind"
SelectCommand="Select ProductName,UnitPrice,UnitsInStock FROM Products"
Runat="Server" />
</form>
</body>
</html>
The page in Listing 4 displays the contents of the Products database table. It contains a GridView control that is bound to a SqlDataSource control. The SqlDataSource control connects to the Northwind database table in order to retrieve the database data.
You should be aware of an important limitation when using Skins with the GridView control. A Skin cannot contain any script. This means that you cannot use a TemplateField that contains a databound expression within a Skin. It also means that you cannot declare and use any methods within a Skin.
Default Skins vs. Named Skins
In the previous section, you learned how to create a skin that applies to all instances of a control of a certain type. For example, you learned how to create a skin that applies to all TextBox controls or GridView controls. However, you'll quickly discover that you want to create more than one skin for the same control. For example, you'll want to be able to apply different skins to a GridView control on different pages (or even within the same page).
You can create more than one skin that applies to the same type of control if you create a "named skin." A named skin is a skin that includes a SkinID. For example, the Skin file in Listing 5 contains three skins that can be applied to a TextBox control.
Listing 5. TextBox.Skin
<asp:TextBox
BackColor="Green"
Runat="Server" />
<asp:TextBox
SkinID="BlueTextBox"
BackColor="Blue"
Runat="Server" />
<asp:TextBox
SkinID="RedTextBox"
BackColor="Red"
Runat="Server" />
In Listing 5, the first TextBox skin is called the default skin. Since the skin does not include a SkinID, the skin is applied by default to all TextBox controls. The second and third TextBox skins, on the other hand, do include values for their SkinID properties. These skins are applied to a TextBox only when they are explicitly referenced by their SkinID values.
For example, the page in Listing 6 uses all three skins from the TextBox.Skin Skin file.
Listing 6. SkinnedTextBoxes.aspx
<%@ Page Theme="OrangeTheme" %>
<html>
<head runat="server">
<title>Skinned TextBoxes</title>
</head>
<body>
<form id="form1" runat="server">
<asp:TextBox
id="TextBox1"
Runat="Server" />
<br />
<asp:TextBox
id="TextBox2"
SkinID="BlueTextBox"
Runat="Server" />
<br />
<asp:TextBox
id="TextBox3"
SkinID="RedTextBox"
Runat="Server" />
</form>
</body>
</html>
Since the first TextBox control in Listing 6 does not include a SkinID property, it is skinned with the default skin. The second TextBox is skinned with the BlueTextBox skin and the final TextBox is skinned with the RedTextBox skin (see Figure 3).
.gif)
Figure 3. Applying default and named skins
Applying a Theme to an Entire Application
To this point, we've applied themes to individual pages by using the theme attribute of the Page directive. You can, if you need, apply a theme to an entire application within a Web configuration file.
For example, the Web configuration file in Listing 7 applies the OrangeTheme to every page in the application.
Listing 7. Web.Config
<configuration>
<system.web>
<pages theme="OrangeTheme" />
</system.web>
</configuration>
The Web.Config file in Listing 7 will apply the OrangeTheme to all pages that do not already have a theme specified in their Page directive. In other words, the Page directive overrides any theme specified in a Web.Config file.
The same application can contain more than one Web.Config file that specifies a theme. You can add distinct Web configuration files to different subfolders and each Web configuration file can specify a different theme.
Themes vs. StyleSheetThemes
When you apply a theme to a page, any control property set in the theme takes precedence over any property set in the page. For example, if a theme specifies that all TextBox controls should appear with an orange background, then every TextBox in a page will appear with an orange background even when an individual TextBox control has a different value for its BackColor property.
There are circumstances, however, in which you'll want to override a particular appearance setting within a page. For example, you might want to highlight a particular TextBox when there are multiple TextBox controls in a page by changing its BackColor to red. In these circumstances, you need to take advantage of a StyleSheetTheme. A StyleSheetTheme works just like a normal theme except for the fact that it can be overridden by individual control properties.
A StyleSheetTheme works more like a cascading style sheet than it works like a normal theme. In the same way that you can override a cascading style sheet rule with a style rule specified for an individual HTML tag, you can override a StyleSheetTheme property setting with a property setting for an individual control.
For example, the Skin file in Listing 8 contains a TextBox skin that sets every TextBox control's BackColor to orange and ForeColor to green.
Listing 8. TextBox.Skin
<asp:TextBox
BackColor="Orange"
ForeColor="Green"
Runat="Server" />
Imagine that the skin in Listing 8 has been added to the OrangeTheme theme. The page in Listing 9 applies the skin using the StyleSheetTheme attribute rather than the theme attribute.
Listing 9. StyleSheetThemedTextBox.aspx
<%@ Page StyleSheetTheme="OrangeTheme" %>
<html>
<head runat="server">
<title>Style Sheet Themed TextBox</title>
</head>
<body>
<form id="form1" runat="server">
<b>First Name:</b>
<asp:TextBox
ID="txtFirstName"
Runat="Server" />
<br /><br />
<b>Last Name:</b>
<asp:TextBox
ID="txtLastName"
BackColor="Yellow"
Runat="Server" />
<br /><br />
<asp:Button
Text="Submit"
Runat="Server" />
</form>
</body>
</html>
When you open the page in Listing 9, the first TextBox control appears with an orange BackColor and the second TextBox control appears with a yellow BackColor (see Figure 4). The BackColor of the first TextBox control is set by the StyleSheetTheme attribute that appears in the Page directive. The value of the BackColor property of the second TextBox control is set in the page itself.
You can apply both the StyleSheetTheme and the Theme attribute to the same page. In that case, the Theme attribute will take precedence. For example, if you apply a Theme to your application in the Web Configuration file, then the Theme settings will override the StyleSheetTheme settings.
.gif)
Figure 4. Using a StyleSheetTheme
Images and Themes
You can include images with a theme. Adding images to a theme is useful when working with controls such as the Menu, TreeView, or BulletedList control. You can even use a theme to provide a default image for the ASP.NET Image control.
For example, Listing 10 contains a skin for a BulletedList control. The skin includes a reference to an image named OrangeBullet located in the BulletImages subfolder of the OrangeTheme folder.
Listing 10. BulletedList.Skin
<asp:BulletedList
BulletStyle="CustomImage"
BulletImageUrl="BulletImages/OrangeBullet.gif"
Runat="Server" />
The page in Listing 11 uses the BulletedList skin to display a bulleted list of book titles from the Titles database table.
Listing 11. ShowBulletedList.aspx
<%@ Page Theme="OrangeTheme" %>
<html>
<head runat="server">
<title>Show BulletedList</title>
</head>
<body>
<form id="form1" runat="server">
<asp:BulletedList
ID="BulletedTitles"
DataSourceID="TitleSource"
DataTextField="Title"
Runat="Server" />
<asp:SqlDataSource
ID="TitleSource"
ConnectionString="Server=localhost;Trusted_Connection=true;Database=Pubs"
SelectCommand="SELECT Title FROM Titles"
Runat="Server" />
</form>
</body>
</html>
When the page in Listing 11 is opened, the list of titles is retrieved from the Titles database table and displayed by the BulletedList control. Notice that the OrangeBullet image is applied to the BulletedList control by the BulletList skin (see Figure 5).
.gif)
Figure 5. Applying a theme that includes images
Cascading Style Sheets and Themes
You can also use cascading style sheets with themes. If you add a cascading style sheet to a theme folder, then the cascading style sheet is automatically applied to a page when you apply the theme to a page. When the contents of the Themes folder is compiled into a class, any cascading style sheets found in the Themes folder are automatically referenced by the class.
For example, imagine that you want to create a hover effect for all of the hyperlinks in a page. You can use the cascading style sheet in Listing 12 in order to change the color of a hyperlink to orange when a mouse hovers over the hyperlink.
Listing 12. Hover.css
A:hover
{
color: orange;
}
If you add the cascading style sheet in Listing 12 to the OrangeTheme folder, then the style sheet will be automatically applied to a page when the OrangeTheme is applied to the page. The one requirement for this to work is that the page must include a server-side <head runat="Server" /> tag. This tag renders the link to the style sheet.
Since the page in Listing 13 includes the <head runat="Server" /> tag and it has the OrangeTheme applied to it, the page is automatically linked to the Hover.css style sheet.
Listing 13. Menu.aspx
<%@ Page Theme="OrangeTheme" %>
<html>
<head runat="server">
<title>Menu</title>
</head>
<body>
<form id="form1" runat="server">
<a href="Home.aspx">Home</a>
<br />
<a href="Products.aspx">Products</a>
<br />
<a href="Services.aspx">Services</a>
</form>
</body>
</html>
If you open the page in Listing 13 and hover your mouse over a hyperlink, the hyperlink will change to the color orange (see Figure 6).
.gif)
Figure 6. Using cascading style sheets with themes
You can include more than one cascading style sheet in a theme. If you add multiple cascading style sheets, then the server-side <head runat="Server"/> tag will automatically generate links for each style sheet.
Dynamically Loading Themes
Imagine that you want to dynamically change the appearance of your Web site. You might want to enable users to customize the colors and general appearance of your Web application while they interact with it. You can provide this power to users of your ASP.NET 2.0 applications by taking advantage of dynamically loaded themes.
You can modify the theme used by a page at run time by modifying the value of the theme property of the Page object. You can assign the name of any valid theme to this property. You must be aware of one restriction when using the theme property. The theme property can only be set during or before the Page PreInit event.
For example, imagine that you create two new theme folders named RedTheme and YellowTheme within the App_Themes folder. You can add the skin in Listing 14 to the RedTheme folder and the skin in Listing 15 to the YellowTheme folder (the skins change the BackColor of the DropDownList control to red or yellow).
Listing 14. RedTheme/DropDownList.Skin
<asp:DropDownList
BackColor="Red"
Runat="Server" />
Listing 15. YellowTheme/DropDownList.Skin
<asp:DropDownList
BackColor="Yellow"
Runat="Server" />
The page in Listing 16 dynamically loads either the RedTheme or the YellowTheme depending on the user's selection from a DropDownList control (see Figure 7). Notice that the Request collection is used to retrieve the user's selection within the PreInit event handler. You must use the Request collection since the PreInit event is raised too early in the page execution lifecycle to use any of the properties of the dropTheme DropDownList control.
.gif)
Figure 7. Dynamically loading a theme
Listing 16. DynamicTheme.aspx (Visual Basic .NET)
<%@ Page Language="VB" %>
<script runat="server">
Sub Page_PreInit(ByVal sender As Object, ByVal e As EventArgs)
Page.Theme = Request("dropTheme")
End Sub
</script>
<html>
<head id="Head1" runat="server">
<title>Dynamic Theme</title>
</head>
<body>
<form id="form1" runat="server">
<asp:DropDownList
id="dropTheme"
AutoPostBack="true"
Runat="Server">
<asp:ListItem Text="YellowTheme" />
<asp:ListItem Text="RedTheme" />
</asp:DropDownList>
</form>
</body>
</html>
Listing 16. DynamicTheme.aspx (C#)
<%@ Page Language="C#" %>
<script runat="server">
void Page_PreInit(object sender, EventArgs e)
{
Page.Theme = Request["dropTheme"];
}
</script>
<html>
<head runat="server">
<title>Dynamic Theme</title>
</head>
<body>
<form id="form1" runat="server">
<asp:DropDownList
id="dropTheme"
AutoPostBack="true"
Runat="Server">
<asp:ListItem Text="YellowTheme" />
<asp:ListItem Text="RedTheme" />
</asp:DropDownList>
</form>
</body>
</html>
Conclusion
In this article, we've examined both basic and advanced applications of ASP.NET 2.0 themes. Themes are a powerful addition to the ASP.NET framework. By taking advantage of themes, you can dramatically reduce the amount of content that you need to add to individual ASP.NET pages. Themes enable you to define the appearance of a control once and apply the appearance throughout a Web application. Therefore, themes enable you to easily create Web sites that have a consistent and maintainable design.
Related Books
About the author
Stephen Walther wrote the best-selling book on ASP.NET, ASP.NET Unleashed. He was also the architect and lead developer of the ASP.NET Community Starter Kit, a sample ASP.NET application produced by Microsoft. He has provided ASP.NET training to companies across the United States, including NASA and Microsoft, through his company Superexpert (http://www.superexpert.com).