Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Pro ASP.NET 2.0 In CSharp 2005 (2005) [eng]

.pdf
Скачиваний:
107
Добавлен:
16.08.2013
Размер:
29.8 Mб
Скачать

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

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

529

rules. In addition, if you’ve crafted the perfect stylesheet, you can still use it. It’s up to you whether you want to use one or both solutions. As you’ll see later in this chapter (in the section “Using CSS in a Theme”), it’s possible to use a stylesheet as part of a theme.

Theme Folders and Skins

All themes are application-specific. To use a theme in a web application, you need to create a folder that defines it. You need to place this folder in a folder named App_Theme, which must be inside the top-level directory for your web application. In other words, a web application named SuperCommerce might have a FunkyTheme theme in the SuperCommerce\App_Theme\FunkyTheme folder.

An application can contain definitions for multiple themes, as long as each theme is in a separate folder. Only one theme can be active on a given page at a time. In the “Applying Themes Dynamically” section, you’ll discover how you can dynamically change the active theme when your page is processing.

To actually make your theme accomplish something, you need to create at least one skin file in the theme folder. A skin file is a text file with the .skin extension. ASP.NET never serves skin files directly—instead, they’re used behind the scenes to define a theme.

A skin file is essentially a list of control tags—with a twist. The control tags in a skin file don’t need to completely define the control. Instead, they need to set only the properties you want to standardize. For example, if you’re trying to apply a consistent color scheme, you might be interested in setting properties such as ForeColor and BackColor only. When you add a control tag for the ListBox control, it might look like this:

<asp:ListBox runat="server" ForeColor="White" BackColor="Orange"/>

The runat="server" portion is always required. Everything else is optional. The id attribute is not allowed in a theme, because it’s required to uniquely identify each control.

It’s up to you whether you create multiple skin files or place all your control tags in a single skin file. Both approaches are equivalent, because ASP.NET treats all the skin files in a theme directory as part of the same theme definition. Often, it makes sense to separate the control tags for complex controls (such as the data controls) into separate skin files. Figure 15-2 shows the relationship between themes and skins in more detail.

Figure 15-2. Themes and skins

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" />

534 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-6 shows how this Calendar control would ordinarily look and how it looks when the page uses the corresponding theme.

Figure 15-6. An unformatted Calendar on an unthemed and themed page

Caution When you create skins that specify details such as sizing, be careful. When these settings are applied to a page, they could cause the layout to change with unintended consequences. If you’re in doubt, set a SkinID so that the skin is applied only if the control specifically opts in.

Another powerful technique is to reuse images by making them part of your theme. For example, imagine you perfect an image that you want to use for OK buttons throughout your website and you have another image for all the cancel buttons. The first step in implementing this design is to add the images to your theme folder. For the best organization, it makes sense to create one or more subfolders just for holding images. In Figure 15-7, the images are stored in a folder named ButtonImages.

Figure 15-7. Adding images to a theme

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

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

537

{

Page.Theme = (string)Session["Theme"];

}

}

Of course, you could also store the selected theme in a cookie, a session state, a profile (see Chapter 24), or any other user-specific location.

If you want to create a page that allows the user to choose a theme, you need a little more sleight of hand. The problem is that the user’s selection can’t be read until after the page has been loaded and has passed the PreInit stage. However, at this point, it is too late to set the theme. One way around this problem is to trigger a refresh by redirecting the page back to itself. The most efficient way to accomplish this is to use Server.Transfer() so that all the processing takes place on the server. (Response.Redirect() sends a redirect header to the client and so requires an extra round-trip.)

Here’s the code that presents the list of selections when the page loads and then records the selection and transfers the page when a button is clicked:

protected void Page_Load(object sender, EventArgs e)

{

if (!Page.IsPostBack)

{

//Fill the list box with available themes

//by reading the folders in the App_Themes folder.

DirectoryInfo themeDir = new DirectoryInfo(Server.MapPath("App_Themes")); lstThemes.DataTextField = "Name";

lstThemes.DataSource = themeDir.GetDirectories(); lstThemes.DataBind();

}

}

protected void cmdApply_Click(object sender, EventArgs e)

{

// Set the chosen theme.

Session["Theme"] = lstThemes.SelectedValue;

// Refresh the page. Server.Transfer(Request.FilePath);

}

Figure 15-8 shows the resulting page.

Figure 15-8. Allowing the user to choose a theme