Pro ASP.NET 2.0 In CSharp 2005 (2005) [eng]
.pdf
528 C H A P T E R 1 5 ■ T H E M E S A N D M A S T E R PA G E S
You can also create rules that are applied to HTML tags automatically. To do this, specify the tag name for the rule name. Here’s a rule that affects all <h2> tags on the page that uses the stylesheet:
h2
{ ... }
Although this automatic stylesheet application sounds useful, it’s less convenient in ASP.NET because you’re usually dealing with controls, not individual HTML tags. You can’t always be certain what tags will be used to render a given control, so it’s best to explicitly specify the rule you want to use through the class name.
Using stylesheets accomplishes two things. First, it standardizes your layout so that you can quickly format new pages without introducing minor mistakes or idiosyncrasies. Second, it separates the formatting information so that it doesn’t appear in your web pages at all, allowing you to modify the format without tracking down each page or recompiling your code. And although CSS isn’t a .NET-centric standard, Visual Studio still provides rich support for it.
Themes
With the convenience of CSS styles, you might wonder why developers need anything more. The problem is that CSS rules are limited to a fixed set of style attributes. They allow you to reuse specific formatting details (fonts, borders, foreground and background colors, and so on), but they obviously can’t control other aspects of ASP.NET controls. For example, the CheckBoxList control includes properties that control how it organizes items into rows and columns. Although these properties affect the visual appearance of the control, they’re outside the scope of CSS, so you need to set them by hand. Additionally, you might want to define part of the behavior of the control along with the formatting. For example, you might want to standardize the selection mode of a Calendar control or the wrapping in a TextBox. This obviously isn’t possible through CSS.
Themes, a new feature in ASP.NET 2.0, fill this gap. Like CSS, themes allow you to define a set of style attributes that you can apply to controls in multiple pages. However, unlike CSS, themes aren’t implemented by the browser. Instead, they’re a native ASP.NET solution that’s implemented on the server. Although themes don’t replace styles, they have some features that CSS can’t provide. Here are the key differences:
Themes are control-based, not HTML-based: As a result, themes allow you to define and reuse almost any control property. For example, themes allow you to specify a set of common node pictures and use them in numerous TreeView controls or to define a set of templates for multiple GridView controls. CSS is limited to style attributes that apply directly to HTML.
Themes are applied on the server: When a theme is applied to a page, the final styled page is sent to the user. When a stylesheet is used, the browser receives both the page and the style information and then combines them on the client side.
Themes can be applied through configuration files: This lets you apply a theme to an entire folder or your whole website without modifying a single web page.
Themes don’t cascade in the same way as CSS: Essentially, if you specify a property in a theme and in the individual control, the value in the theme overwrites the property in the control. However, you have the choice of changing this behavior and giving precedence to the properties in the page, which makes themes behave more like stylesheets.
It would be overstating it to say that themes replace CSS. Instead, themes represent a higherlevel model. To implement your formatting properties, ASP.NET will frequently render inline style
530 C H A P T E R 1 5 ■ T H E M E S A N D M A S T E R PA G E S
ASP.NET also supports global themes. These are themes you place in the [WinDir]\ Microsoft.Net\Framework\[Version]\Themes folder. However, it’s recommended that you use local themes, even if you want to create more than one website that has the same theme. Using local themes makes it easier to deploy your web application, and it gives you the flexibility of introducing site-specific differences in the future.
If you have a local theme with the same name as a global theme, the local theme takes precedence, and the global theme is ignored. The themes are not merged together.
■Tip ASP.NET doesn’t ship with any predefined themes. That means you’ll need to create your own from scratch or download sample themes from other websites such as http://www.asp.net.
Applying a Simple Theme
To add a theme to your project, select Website Add New Item and choose Skin File. Visual Studio will warn you that skin files need to be placed in a subfolder of the App_Themes folder and will ask you if that’s what you intended. If you choose Yes, Visual Studio will create a folder with the same name as your theme file. You can then rename the folder and the file to whatever you’d like to use. Figure 15-3 shows an example with a theme that contains a single skin file.
Figure 15-3. A theme in the Solution Explorer
Visual Studio doesn’t include any design-time support for creating themes, so it’s up to you to copy and paste control tags from other web pages. Here’s a sample skin that sets background and foreground colors for several common controls:
<asp:ListBox runat="server" ForeColor="White" BackColor="Orange"/> <asp:TextBox runat="server" ForeColor="White" BackColor="Orange"/> <asp:Button runat="server" ForeColor="White" BackColor="Orange"/>
To apply the theme in a web page, you need to set the Theme attribute of the Page directive to the folder name for your theme. (ASP.NET will automatically scan all the skin files in that theme.)
<%@ Page Language="C#" AutoEventWireup="true" ... Theme="FunkyTheme" %>
You can make this change by hand, or you can select the DOCUMENT object in the Properties window at design time and then set the Theme property (which provides a handy drop-down list of all your web application’s themes). Visual Studio will modify the Page directive accordingly.
C H A P T E R 1 5 ■ T H E M E S A N D M A S T E R PA G E S |
531 |
When you apply a theme to a page, ASP.NET considers each control on your web page and checks your skin files to see if they define any properties for that control. If ASP.NET finds a matching tag in the skin file, the information from the skin file overrides the current properties of the control.
Figure 15-4 shows the result of applying the FunkyTheme to a simple page. The first picture shows the Themes.aspx page in its natural state, with no theme. The second picture shows the same page with the FunkyTheme applied. All the settings in FunkyTheme are applied to the controls in Themes.aspx, even if they overwrite values you’ve explicitly set in the page (such as the background for the list box). However, details that were in the original page but that don’t conflict with the theme (such as the custom font for the buttons) are left in place.
Figure 15-4. A simple page before and after applying a theme
Handling Theme Conflicts
As you’ve seen, when properties conflict between your controls and your theme, the theme wins. However, in some cases you might want to change this behavior so that your controls can fine-tune a theme by specifically overriding certain details. ASP.NET gives you this option, but it’s an all-or- nothing setting that applies to all the controls on the entire page.
To make this change, just use the StyleSheetTheme attribute instead of the Theme attribute in the Page directive. (The StyleSheetTheme setting works more like CSS.) Here’s an example:
<%@ Page Language="C#" AutoEventWireup="true" ... StyleSheetTheme="FunkyTheme" %>
Now the custom yellow background of the ListBox takes precedence over the background color specified by the theme. Figure 15-5 shows the result—and a potential problem. Because the foreground color has been changed to white, the lettering is now difficult to read. Overlapping formatting specifications can cause glitches such as this, which is why it’s often better to let your themes take complete control by using the Theme attribute.
■Note It’s possible to use both the Theme attribute and the StyleSheetTheme attribute at the same time so that some settings are always applied (those in the Theme) and others are applied only if they aren’t already specified in the control (those in the StyleSheetTheme). Depending on your point of view (and level of comfort with themes and styles), this is either a terribly confusing design or a useful way to make a distinction between settings you want to enforce (Theme) and settings you want to use as defaults (StyleSheetTheme).
532 C H A P T E R 1 5 ■ T H E M E S A N D M A S T E R PA G E S
Figure 15-5. Giving the control tag precedence over the theme
Another option is to configure specific controls so they opt out of the theming process entirely. To do this, simply set the EnableTheming property of the control to false. ASP.NET will still apply the theme to other controls on the page, but it will skip over the control you’ve configured.
<asp:Button ID="Button1" runat="server" ... EnableTheming="false" />
Creating Multiple Skins for the Same Control
Having each control locked into a single format is great for standardization, but it’s probably not flexible enough for a real-world application. For example, you might have several types of text boxes that are distinguished based on where they’re used or what type of data they contain. Labels are even more likely to differ, depending on whether they’re being used for headings or for body text. Fortunately, ASP.NET allows you to create multiple declarations for the same control.
Ordinarily, if you create more than one theme for the same control, ASP.NET will give you a build error stating that you can have only a single default skin for each control. To get around this problem, you need to create a named skin by supplying a SkinID attribute. Here’s an example:
<asp:ListBox runat="server" ForeColor="White" BackColor="Orange" /> <asp:TextBox runat="server" ForeColor="White" BackColor="Orange" /> <asp:Button runat="server" ForeColor="White" BackColor="Orange" /> <asp:TextBox runat="server" ForeColor="White" BackColor="DarkOrange" Font-Bold="True" SkinID="Dramatic"/>
<asp:Button runat="server" ForeColor="White" BackColor="DarkOrange" Font-Bold="True" SkinID="Dramatic"/>
The catch is that named skins aren’t applied automatically like default skins. To use a named skin, you need to set the SkinID of the control on your web page to match. You can choose this value from a drop-down list that Visual Studio creates based on all your defined skin names, or you can type it in by hand:
<asp:Button ID="Button1" runat="server" ... SkinID="Dramatic" />
If you don’t like the opt-in model for themes, you can make all your skins named. That way, they’ll never be applied unless you set the control’s SkinID.
C H A P T E R 1 5 ■ T H E M E S A N D M A S T E R PA G E S |
533 |
■Note Using named themes is similar to using CSS rules that are based on class name (as shown at the beginning of this chapter). CSS class rules are applied only if you set the class attribute of the corresponding HTML tag.
ASP.NET is intelligent enough to catch if you try to use a skin name that doesn’t exist, in which case you’ll get a build warning. The control will then behave as though you set EnableTheming to false, which means it will ignore the corresponding default skin.
■Tip The SkinID doesn’t need to be unique. It just has to be unique for each control. For example, imagine you want to create an alternate set of skinned controls that use a slightly smaller font. These controls match your overall theme, but they’re useful on pages that display a large amount of information. In this case, you can create new Button, TextBox, and Label controls and give each one the same skin name (such as Smaller).
Skins with Templates and Images
So far, the theme examples have applied relatively simple properties. However, you can create much more detailed control tags in your skin file. Most control properties support themes. If a property can’t be declared in a theme, you’ll receive a build error when you attempt to launch your application.
■Note Control developers can choose which properties you can set in a skin file by applying the Themeable attribute to the property declaration. If this attribute isn’t present, the property can’t be set in a theme. You’ll learn more about custom control attributes in Chapter 28.
For example, many controls support styles that specify a range of formatting information. The data controls are one example, and the Calendar control provides another. Here’s how you might define Calendar styles in a skin file to match your theme:
<asp:Calendar ID="Calendar1" runat="server" BackColor="White" ForeColor="Black" BorderColor="Black" BorderStyle="Solid" CellSpacing="1"
Font-Names="Verdana" Font-Size="9pt" Height="250px" Width="500px" NextPrevFormat="ShortMonth" SelectionMode="Day"> <SelectedDayStyle BackColor="DarkOrange" ForeColor="White" />
<DayStyle BackColor="Orange" Font-Bold="True" ForeColor="White" /> <NextPrevStyle Font-Bold="True" Font-Size="8pt" ForeColor="White" /> <DayHeaderStyle Font-Bold="True" Font-Size="8pt" ForeColor="#333333" Height="8pt" />
<TitleStyle BackColor="Firebrick" BorderStyle="None" Font-Bold="True" Font-Size="12pt" ForeColor="White" Height="12pt" /> <OtherMonthDayStyle BackColor="NavajoWhite" Font-Bold="False" ForeColor="DarkGray" />
</asp:Calendar>
This skin defines the font, colors, and styles of the Calendar. It also sets the selection mode, the formatting of the month navigation links, and the overall size of the calendar. As a result, all you need to use this formatted calendar is the following streamlined tag:
<asp:Calendar ID="Calendar1" runat="server" />
C H A P T E R 1 5 ■ T H E M E S A N D M A S T E R PA G E S |
535 |
Now you need to create the skins that use these images. In this case, both of these tags should be named skins. That’s because you’re defining a specific type of standardized button that should be available to the page when needed. You aren’t defining a default style that should apply to all buttons.
<asp:ImageButton runat="server" SkinID="OKButton" ImageUrl="ButtonImages/buttonOK.jpg" /> <asp:ImageButton runat="server" SkinID="CancelButton" ImageUrl="ButtonImages/buttonCancel.jpg" />
When you add a reference to an image in a skin file, always make sure the image URL is relative to the theme folder, not the folder where the page is stored. When this theme is applied to a control, ASP.NET automatically inserts the Themes\ThemeName portion at the beginning of the URL.
Now to apply these images, simply create an ImageButton in your web page that references the corresponding skin name:
<asp:ImageButton ID="ImageButton1" runat="server" SkinID="OKButton" /> <asp:ImageButton ID="ImageButton2" runat="server" SkinID="CancelButton" />
You can use the same technique to create skins for other controls that use images. For example, you can standardize the node pictures used in a TreeView, the bullet image used for the BulletList control, or the icons used in a DataGridView.
Using CSS in a Theme
ASP.NET also gives you the ability to use a stylesheet as part of a theme. You might use this feature for a few reasons:
•You want to style HTML elements that might not correspond to server controls.
•You prefer to use a stylesheet because it is more standardized or because it can also be used to format static HTML pages.
•You have already invested effort in creating a stylesheet, and you don’t want to create themes to implement the same formatting.
To use a stylesheet in a theme, you first need to add the stylesheet to your theme folder. ASP.NET searches this folder for all .css files and dynamically binds them to any page that uses the theme.
This has one catch, however. To bind the page to the stylesheet, ASP.NET needs to be able to insert a <link> tag in the <head> section of the web page. This is possible only if the <head> tag has the runat="server" attribute. This turns the <head> element into a server-side control that ASP.NET can modify to insert the stylesheet links.
<head runat="server"> <title>...</title>
</head>
Once this is in place, you simply need to set the Theme attribute of the page to gain access to the stylesheet rules. You can then set the CssClass property of the controls you want to format, as you saw earlier in the chapter. Any style rules that are linked directly to HTML tags are applied automatically.
You can use as many stylesheets as you want in a theme. ASP.NET will add multiple <link> tags, one for each stylesheet in the theme.
You can include more than one cascading stylesheet in a theme. If you add multiple cascading stylesheets, then the server-side <head runat="Server"/> tag will automatically generate links for each stylesheet.
536 C H A P T E R 1 5 ■ T H E M E S A N D M A S T E R PA G E S
Applying Themes Through a Configuration File
Using the Page directive, you can bind a theme to a single page. However, you might decide that your theme is ready to be rolled out for the entire web application. The cleanest way to apply this theme is to configure the <pages> element in the web.config file for your application, as shown here:
<configuration>
<system.web>
<pages theme="FunkyTheme" /> </system.web>
</configuration>
If you want to use the stylesheet behavior so that the theme doesn’t overwrite conflicting control properties, use the StyleSheetTheme attribute instead of theme:
<configuration>
<system.web>
<pages StyleSheetTheme="FunkyTheme" /> </system.web>
</configuration>
Either way, when you specify a theme in the web.config file, the theme you specify will be applied throughout all the pages in your website, provided these pages don’t have their own theme settings. If a page specifies the Theme attribute, the page setting will take precedence over the web.config setting.
Using this technique, it’s just as easy to apply a theme to part of a web application. For example, you can create a separate web.config file for each subfolder and use the <pages> setting to configure different themes.
■Tip If you apply themes through a configuration file, you can still disable them for specific pages. Just include the EnableTheming attribute in the Page directive, and set it to false. No themes will be applied to the page.
Applying Themes Dynamically
In some cases, themes aren’t used to standardize website appearance but to make that appearance configurable for each user. In this scenario, your web application gives the user the chance to specify the theme that your pages will use.
This technique is remarkably easy. All you need to do is set the Page.Theme or Page.StyleSheet property dynamically in your code. The trick is that this step needs to be completed in the Page.Init event stage. After this point, attempting to set the property causes a compilation error.
Here’s an example that applies a dynamic theme by reading the theme name from the current Session collection:
protected void Page_PreInit(object sender, EventArgs e)
{
if (Session["Theme"] == null)
{
//No theme has been chosen. Choose a default
//(or set a blank string to make sure no theme
//is used).
Page.Theme = "";
}
else
