Chapter 12. Styles, Themes, and Master Pages

Using the techniques you've learned so far, you can create polished web pages and let users surf from one page to another. However, to integrate your web pages into a unified, consistent website, you need a few more tools. In this chapter, you'll consider three of the most important tools that you can use: styles, themes, and master pages.

Styles are part of the Cascading Style Sheet (CSS) standard. They aren't directly tied to ASP.NET, but they're still a great help in applying consistent formatting across your entire website. With styles, you can define a set of formatting options once and reuse it to format different elements on multiple pages. You can even create styles that apply their magic automatically—for example, styles that change the font of all the text in your website without requiring you to modify any of the web page code. Best of all, once you've standardized on a specific set of styles and applied them to multiple pages, you can give your entire website a face-lift just by editing your style sheet.

Styles are genuinely useful, but there are some things they just can't do. Because styles are based on the HTML standard, they have no understanding of ASP.NET concepts like control properties. To fill the gap, ASP.NET includes a themes feature, which plays a similar role to styles but works exclusively with server controls. Much as you use styles to automatically set the formatting characteristics of HTML elements, you use themes to automatically set the properties of ASP.NET controls.

Another feature for standardizing websites is master pages. Essentially, a master page is a blueprint for part of your website. Using a master page, you can define web page layout, complete with all the usual details such as headers, menu bars, and ad banners. Once you've perfected a master page, you can use it to create content pages. Each content page automatically acquires the layout and the content of the linked master page.

By using styles, themes, and master pages, you can ensure that all the pages on your website share a standardized look and layout. In many cases, these details are the difference between an average website and one that looks truly professional.

Styles

In the early days of the Internet, website designers used the formatting features of HTML to decorate these pages. These formatting features were limited, inconsistent, and sometimes poorly supported. Worst of all, HTML formatting led to horribly messy markup, with formatting details littered everywhere.

The solution is the CSS standard, which is supported (with some discrepancies) in all modern browsers. Essentially, CSS gives you a wide range of consistent formatting properties that you can apply to any HTML element. Styles allow you to add borders, set font details, change colors, add margin space and padding, and so on. Many of the examples you've seen so far have in this book have used CSS formatting.

In the following sections, you'll learn the basics of the CSS standard. You'll see how web controls use CSS to apply their formatting, and you'll learn how you can explicitly use styles in your ASP.NET web pages.

Note

The CSS standard is not a single, universal standard with complete commonality across all browsers. Instead, it exists in several versions (up to CSS3) and includes features that not all browsers support and features that browsers implement differently. In this chapter, you'll focus on subset of CSS that's well supported, with a high level of consistency in all modern browsers. However, before you attempt to use CSS, make sure you've chosen a suitable doctype, as described in Chapter 4, which ensures your page won't be rendered in Internet Explorer's quirks mode, which can lead to significantly different formatting.

Style Types

Web pages can use styles in three different ways:

  • Inline style: An inline style is a style that's placed directly inside an HTML tag. This can get messy, but it's a reasonable approach for one-time formatting. You can remove the style and put it in a style sheet later.

  • Internal style sheet: An internal style sheet is a collection of styles that are placed in the <head> section of your web page markup. You can then use the styles from this style sheet to format the web controls on that page. By using an internal style sheet, you get a clear separation between formatting (your styles) and your content (the rest of your HTML markup). You can also reuse the same style for multiple elements.

  • External style sheet: An external style sheet is similar to an internal style sheet, except it's placed in a completely separate file. This is the most powerful approach, because it gives you a way to apply the same style rules to many pages.

You can use all types of styles with ASP.NET web pages. You'll see how in the following sections.

Creating a Basic Inline Style

To apply a style to an ordinary HTML element, you set the style attribute. Here's an example that gives a blue background to a paragraph:

<p style="background: Blue">This text has a blue background.</p>

Every style consists of a list of one or more formatting properties. In the preceding example, the style has a single formatting property, named background, which is set to the value Blue. To add multiple style properties, you simply separate them with semicolons, as shown here:

<p style="color:White; background:Blue; font-size:x-large; padding:10px">
This text has a blue background.</p>

This style creates large white text with a blue background and 10 pixels of spacing between the edge of the element (the blue box) and the text content inside.

Note

The full list of formatting properties is beyond the scope of this book (although you can get all the details at http://www.w3schools.com/css). However, you'll soon see that Visual Studio includes tools that can help you build the styles you want, so you don't need to remember style property names or write styles by hand.

You can use the same approach to apply formatting to a web control using a style. However, you don't need to, because web controls provide formatting properties. For example, if you create a Label control like this:

<asp:Label ID="MyLabel" runat="server" ForeColor="White" BackColor="Blue"
 Font-Size="X-Large">Formatted Text</asp:Label>

it's actually rendered into this HTML, which uses an inline style:

<span id="MyLabel" style="color:White; background-color:Blue; font-size:X-Large">
Formatted Text</span>

Incidentally, if you specify a theme and set formatting properties that overlap with your style, the properties have the final say.

The Style Builder

Visual Studio provides an indispensable style builder that lets you create styles by picking and choosing your style preferences in a dedicated dialog box. To try it out, begin by creating a new page in Visual Studio. Then drop a few controls onto your page (for example, a label, text box, and button).

Every new page starts with an empty <div> element. This <div> is simply a content container—by default, it doesn't have any appearance. However, by applying style settings to the <div>, you can create a bordered content region, and you can change the font and colors of the content inside. In this example, you'll see how to use Visual Studio to build a style for the <div> element.

Note

CSS supports a feature it calls inheritance. With inheritance, some formatting properties (such as the font family) are passed down from a parent element to other nested elements. In other words, if you set the font family for a <div> element, all the elements inside will inherit the same font (unless they explicitly specify otherwise). Other properties, like margin and padding settings, don't use inheritance. To learn more about this behavior and the specific properties that use inheritance, you can experiment on your own, consult a dedicated book such as Eric Meyer's CSS: The Definitive Guide (O'Reilly), or use the tutorials at http://www.w3schools.com/css.

For example, imagine you want to create a simple page that includes a few controls, such as the label, text box, and button shown in Figure 12-1. Before formatting the page, make sure all your controls are nested inside the <div> element. Your markup should look something like this:

<div>
    <asp:Label ID="Label1" runat="server">Type something here:
    </asp:Label>
    <asp:TextBox ID="TextBox1" runat="server">
    </asp:TextBox>
    <br /><br />
    <asp:Button ID="Button1" runat="server" Text="Button">
    </asp:Button>
</div>

In the design window, click somewhere inside the <div> (but not on another control). You'll know you're in the right spot when a border appears around your controls, showing you the outline of the <div>, as shown in Figure 12-1.

Adding a style to a <div>

Figure 12.1. Adding a style to a <div>

Next, choose Format

Adding a style to a <div>
Creating an inline style

Figure 12.2. Creating an inline style

To specify style settings, you first need to choose one of the categories in the Category list. For example, if you choose Font you'll see a list of font-related formatting settings, such as font family, font size, text color, and so on. You can apply settings from as many different categories as you want. Table 12-1 provides a brief explanation for each category.

Table 12.1. Style Settings in the New Style Dialog Box

Category

Description

Font

Allows you to choose the font family, font size, and text color, and apply other font characteristics (like italics and bold).

Block

Allows you to fine-tune additional text settings, such as the height of lines in a paragraph, the way text is aligned, the amount of indent in the first list, and the amount of spacing between letters and words.

Background

Allows you to set a background color or image.

Border

Allows you to define borders on one or more edges of the element. You can specify the border style, thickness, and color of each edge.

Box

Allows you to define the margin (the space between the edges of the element and its container) and the padding (the space between the edges of the element and its nested content inside).

Position

Allows you to set a fixed width and height for your element, and use absolute positioning to place your element at a specific position on the page. Use these settings with care. When you make your element a fixed size, there's a danger that the content inside can become too big (in which case it leaks out the bottom or the side). When you position your element using absolute coordinates, there's a chance that it can overlap another element.

Layout

Allows you to control a variety of miscellaneous layout settings. You can specify whether an element is visible or hidden, whether it floats at the side of the page, and what cursor appears when the user moves the mouse overtop, among other settings.

List

If you're configuring a list (a <ul> or <ol> element), you can set the numbering or bullet style. These settings aren't commonly used in ASP.NET web pages, because you're more likely to use ASP.NET list controls like the BulletedList.

Table

Allows you to set details that only apply to table elements (such as <tr> and <td>). For example, you can control whether borders appear around an empty cell.

Note

Remember, you can build a style for any HTML element, not just the <div> element. You'll always get exactly the same New Style dialog box with the same formatting options.

As you make your selections, Visual Studio shows what your style will look like when applied to some sample text (in the Preview box) and the markup that's needed to define your style (in the Description) box. Figure 12-3 shows the New Style dialog box after formatting the <div> into a nicely shaded and bordered box. In the Category list, all the categories with formatting settings are highlighted in bold.

Building a styled division

Figure 12.3. Building a styled division

When you click OK, Visual Studio will add the style information to your element. Here's the markup for the formatted <div> box:

<div style="border-style: solid; border-color: inherit; border-width: 1px;
 padding: 5px; font-size: smaller; font-family: Verdana;
 background-color: #ffffcc">
    <asp:Label ID="Label1" runat="server">Type something here:
    </asp:Label>
    <asp:TextBox ID="TextBox1" runat="server">
    </asp:TextBox>
    <br /><br />
    <asp:Button ID="Button1" runat="server" Text="Button">
    </asp:Button>
</div>

Figure 12-4 shows the final result—a shaded yellow box with a bit of padding and a different font.

Using a styled division

Figure 12.4. Using a styled division

Tip

Be careful you don't give your <div> a fixed size. Ordinarily, the <div> container expands to fit its content. However, if you drag its border in Visual Studio to make the <div> larger, you'll actually end up creating a hard-coded width and height, which are set in the style attribute. The end result is that your <div> container can't expand if its content expands or the web browser window changes size. As a result, your content will leak out of the box.

The CSS Properties Window

Once you've created a style, you have two easy options for modifying it in Visual Studio. Both revolve around the CSS Properties window, which allows you to dissect the formatting details of any style.

To show the CSS Properties window, open a web page in Visual Studio and choose View

The CSS Properties Window
The CSS Properties window (on the left)

Figure 12.5. The CSS Properties window (on the left)

Once the CSS Properties window is visible, you can use it to view one of your styles. First, find the element or web control that uses the style attribute. Then, click to select it in design view. The style information for that element will appear in the CSS Properties window. For example, Figure 12-5 shows what you'll see for the <div> element that was formatted in the previous section.

The CSS Properties window provides an exhaustive list of all the formatting properties you can use in a style. This list is grouped into categories, and the properties in each category are sorted alphabetically. The ones that are currently set are displayed in bold.

You can use the CSS Properties window to modify existing style properties or set new ones, in the same way that you modify the properties for web controls using the Properties window. For example, in Figure 12-5 the font size is being changed.

Depending on your personal taste, you may find that the CSS Properties window is more convenient than the style builder because it gives you access to every style property at once. Or, you may prefer the more organized views in the style builder. (Your preference might also depend on how much screen real estate you have to devote to the CSS Properties window.) If you decide that you want to return to the style builder to change a style, the process is fairly straightforward. First, select the element that has the inline style. Next, look at the Applied Rules list at the top of the CSS Properties window, which should show the text < inline style >. Right-click that text and choose Modify Style to open the Modify Style dialog box, which looks identical to the New Style dialog box you considered earlier.

Note

You can't use the CSS Properties window to create a style. If you select an element that doesn't have a style applied, you won't see anything in the CSS Properties window (unless you select an element that's inside another element, and the containing element uses a style).

Style Inheritance

The CSS Properties window is actually a bit more sophisticated than the current discussion has let on. Not only does it show the style for the current element, it also shows any styles that are applied to containing elements. For example, if you look at an element inside the formatted <div>, you'll see the style that's applied to the <div>. If more than one style is at work (for example, the <div> is contained in another formatted <div>, or you've added a style to the <body> element), you'll see all of these styles in the list, with the most general at the top and the most specific at the bottom. You can select the style you want in the list to modify it.

The CSS Properties shows the styles of nested elements because certain style properties are passed down the element tree through inheritance, such as font settings. Other style properties can indirectly affect a nested element—for example, background colors aren't inherited but because element backgrounds are blank by default, the background of the containing element will show through. It's easy to see this behavior in action—for example, the font and background properties that were applied to the <div> element in the previous example affect the formatting of the elements inside.

When displaying inherited styles, the CSS Properties window will sometimes draw a red line through a property name. It does this if the property is set in the parent element but doesn't apply to the nested element. For example, the nested element may override it with its own style, or the property may not be inherited. Figure 12-6 shows an example with a styled <p> paragraph inside a styled <div>. The style that's inherited from the <div> defines the font-family property (which is inherited), the font-size property (which is inherited but overridden, and is crossed out), and various border properties (which are not inherited, and so are also crossed out).

Inherited and overridden style properties

Figure 12.6. Inherited and overridden style properties

Creating a Style Sheet

To really get the value out of CSS, you need to create a style sheet. To create a style sheet in Visual Studio, choose Website

Creating a Style Sheet

In a style sheet, you define several styles (also known as rules). You can then use these rules to format ordinary HTML and ASP.NET controls. Each rule defines a collection of formatting presets that determines how a single ingredient in your web page should be formatted.

For example, if you want to define a rule for formatting headings, you start by defining a rule with a descriptive name, like this:

.heading1
{
}

Each rule name has two parts. The portion before the period indicates the HTML element to which the rule applies. In this example, nothing appears before the period, which means the rule can apply to any tag. The portion after the period is a unique name (called the CSS class name) that you choose to identify your rule. CSS class names are case sensitive.

Once you've defined a rule, you can add the appropriate formatting information. Here's an example the sets the heading1 style to a large sans-serif font with a green foreground color. The font is set to Verdana (if it's available), or Arial (if it's not), or the browser's default sans-serif typeface (if neither Verdana nor Arial is installed).

.heading1
{
    font-weight: bold;
    font-size: large;
    color: limegreen;
    font-family: Verdana, Arial, Sans-Serif;
}

As you can see, the syntax for defining a style in a style sheet is exactly the same as it is for defining an internal style (not including the rule name and curly braces). By convention, each formatting option in a style is placed on a separate line, but this detail is optional.

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 style sheet:

h2
{ ... }

If you want to apply formatting that applies to the entire web page, you can create a style sheet rule for the <body> element:

body
{ ... }

This gives you a good way to set the default font name and font size.

Fortunately, you don't need to hand-write the rules in your style sheet. Visual Studio allows you to build named styles in a style sheet using the same style builder you used to create inline styles earlier. To use this feature, add a blank style with the right rule name, like this:

.myStyle
{
}

Then, right-click between the two curly braces of an existing style and choose Build Style. You'll see the familiar Modify Style dialog box, where you can point and click your way to custom fonts, borders, backgrounds, and alignment. If you want to create a new style from scratch, simply right-click an empty region of your style sheet and choose Add Style Rule.

A typical style sheet defines a slew of rules. In fact, style sheets are often used to formally define the formatting for every significant piece of a website's user interface. The following style sheet serves this purpose by defining four rules. The first rule sets the font for the <body> element, which ensures that the entire page shares a consistent default font. The rest of the rules are class based, and need to be applied explicitly to the elements that use them. Two rules define size and color formatting for headings, and the final rule configures the formatting that's needed to create a bordered, shaded box of text.

body
{
    font-family: Verdana, Arial, Sans-Serif;
    font-size: small;
}

.heading1
{
    font-weight: bold;
    font-size: large;
    color: lime;
}

.heading2
{
    font-weight: bold;
    font-size: medium;
    font-style: italic;
    color: #C0BA72;
}

.blockText
{
    padding: 10px;
    background-color: #FFFFD9;
    border-style: solid;
    border-width: thin;
}

The CSS Outline Window

Visual Studio includes a CSS Outline window that shows you an overview of the rules in your style sheet. When you're editing a style sheet, you can show the CSS Outline window by choosing View

The CSS Outline Window
Navigating a style sheet with the CSS Outline window

Figure 12.7. Navigating a style sheet with the CSS Outline window

Rule names are technically known as selectors, because they identify the parts of an HTML document that should be selected for formatting. You've seen how to write selectors that use element types, and selectors that use class names. CSS also supports a few more options for building advanced selectors, which aren't described in this chapter. For example, you can create selectors that only apply to a specific element type inside a container (for example, headings in a specific <div> element). Or, you can create selectors that apply formatting to individual elements that have a specific ID value. (These appear in the CSS Outline window under the Element IDs group.) To learn more about CSS, consult a dedicated book such as CSS: The Definitive Guide.

Applying Style Sheet Rules

To use a rule in a web page, you first need to link the page to the appropriate style sheet. You do this by adding a <link> element in the <head> section of your page. The <link> element references the file with the styles you want to use. Here's an example that allows the page to use styles defined in the file StyleSheet.css, assuming it's in the same folder as the web page:

<html xmlns="http://www.w3.org/1999/xhtml">
  <head runat="server">
    <title>...</title>
    <link href="StyleSheet.css" rel="stylesheet" type="text/css" />
  </head>

  <body>
    ...
  </body>
</html>

There's no reason that you need to attach style sheets by hand. An easier option is to drag your style sheet from the Solution Explorer and drop it onto the design surface of your web page (or the <head> section in source view). Visual Studio will insert the <link> element you need automatically.

Tip

There's one more way to connect a web page to a style sheet in your web application: use the menu. While editing your web page, simply select Format

Applying Style Sheet Rules

Once you've added the <link> element, your style rules are available to use in your web page. You can bind any ordinary HTML element or ASP.NET control to your style rules. For example, if you want an ordinary label to use the heading1 format, set the Label.CssClass property to heading1, as shown here:

<asp:Label ID="Label1" runat="server" Text="This Label Uses heading1"
 CssClass="heading1"></asp:Label>

You can also set the CssClass property from the Properties window, in which case you can choose from the list of styles that are defined in the linked style sheet.

To apply a style to an ordinary piece of HTML, you set the class attribute. Here's an example that applies a style to a <div> element, which groups together a paragraph of text for easy formatting:

<div class="blockText" id="paragraph" runat="server" >
  <p>This paragraph uses the blockText style.</p>
</div>

Incidentally, you can add multiple <link> elements to import the styles from several different style sheets. However, you need to be careful that you don't use two style sheets that define styles with the same names.

The Apply Styles Window

Once again, you don't need to edit the markup by hand. You can use Visual Studio's Apply Styles window to attach your style to the elements in a web page. To show the Apply Styles window, open your web page and choose View

The Apply Styles Window

The Apply Styles window shows a list of all the styles that are available in the attached style sheets, along with a preview of each one (see Figure 12-8). To apply a style, simply select an element on your web page and then click the appropriate style in the Apply Styles window. To remove the current style, choose the first option in the list, which is named Clear Styles.

Applying a style with the Apply Styles window

Figure 12.8. Applying a style with the Apply Styles window

Visual Studio is intelligent enough to figure out the appropriate way to apply a style based on what you've selected in your web page:

  • If you select a web control, it adds or changes the CssClass property.

  • If you select an ordinary HTML element, it adds or changes the class attribute.

  • If you select a section of HTML content, it adds a <span> or <div> element (depending on the type of content you've selected) and then sets its class attribute.

Tip

Click the Options button in the Apply Styles window to tweak the way it works. For example, you can choose to preview styles in a different order, or include just those styles that are being used in the current page.

Once you've applied a style, you'll see the result of the new formatting in the design window.

Creating More Styles

In the beginning of this chapter, you saw how you can use the New Style dialog box to create an inline style, which embeds all the formatting in the tag for the affected element (like a <div> element). This gives you all the formatting abilities of CSS, but it creates messy markup that you can't reuse. Inline styles are fine for one-off formatting tasks, but they aren't a good choice for formatting "presets" that you want to reuse throughout your website.

Now that you know how to create an external style sheet, it's worth pointing out that the New Style dialog box also works with them too. In fact, you can use the New Style dialog box to create a new style in an external style sheet while you're editing a web page, in exactly the same way that you create a new inline style. To do this, follow these steps:

  1. Select an element in design view, and choose Format

    Creating More Styles
  2. In the "Define in" box, choose "Existing style sheet," and pick the style sheet file from the URL list.

  3. In the Selector box, type in a period followed by the name of the style you want to create (like .heading2).

  4. Finally, configure the formatting, and click OK.

When you follow this process, Visual Studio adds the style information to the linked style sheet. You don't even need to open the linked CSS file. (Of course, if you still want to create an inline style, no one's stopping you—just choose "(inline style)" in the Selector box, as usual.)

Using style sheets accomplishes two things. First, it standardizes your layout so that you can quickly format 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. 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.

The themes feature fills this gap. Like CSS, themes allow you to define a set of style details that you can apply to controls in multiple pages. However, unlike CSS, themes aren't implemented by the browser. Instead, ASP.NET processes your themes when it creates the page.

Note

Themes don't replace styles. Instead, they complement each other. Styles are particularly useful when you want to apply the same formatting to web controls and ordinary HTML elements. Themes are indispensable when you want to configure control properties that can't be tailored with CSS.

How Themes Work

All themes are application specific. To use a theme in a web application, you need to create a folder that defines it. This folder needs to be placed in a folder named App_Themes, which must be placed inside the top-level directory for your web application. In other words, a web application named SuperCommerce might have a theme named FunkyTheme in the folder SuperCommerce App_ThemesFunkyTheme. 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.

To actually make your theme accomplish anything, 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 that you want to standardize. For example, if you're trying to apply a consistent color scheme, you might be interested in setting only properties such as ForeColor and BackColor. When you add a control tag for the ListBox in the skin file, it might look like this:

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

The runat="server" portion is always required. Everything else is optional. You should avoid setting the ID attribute in your skin, because the page that contains the ListBox needs to define a unique name for the control in the actual web page.

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 put the control tags for complex controls (such as the data controls) in separate skin files. Figure 12-9 shows the relationship between themes and skins in more detail.

Themes and skins

Figure 12.9. Themes and skins

ASP.NET also supports global themes. These are themes you place in the c:Inetpubwwwroot aspnet_clientsystem_webv2.0.50727Themes 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 to introduce 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. This means you'll need to create your own from scratch or download sample themes from websites such as http://www.asp.net.

Applying a Simple Theme

To add a theme to your project, select Website

Applying a Simple Theme
A theme in the Solution Explorer

Figure 12.10. A theme in the Solution Explorer

Unfortunately, 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="VB" AutoEventWireup="False" ... Theme="FunkyTheme" %>

You can make this change by hand, or you can select the DOCUMENT object in the Properties window at design time and 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.

When you apply a theme to a page, ASP.NET considers each control on your web page and checks your skin files to see whether 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. (And if you have a CSS style that affects the same control, it can override the theme, because the theme is set on the server, and the style is applied after, in the browser. However, it's best to avoid this sort of overlapping formatting when possible.)

Figure 12-11 shows the result of applying the FunkyTheme to a simple page. You'll notice that conflicting settings (such as the existing background for the list box) are overwritten. However, changes that don't conflict (such as the custom font for the buttons) are left in place.

A simple page before and after theming

Figure 12.11. A simple page before and after theming

Note

This example demonstrates default themes. When you use this approach, your theme settings won't appear in the Visual Studio design environment. That means you won't be able to see the true appearance of your page until you launch it in your browser. If this poses too much of a problem, consider using the SkinID property (described later in the "Creating Multiple Skins for the Same Control" section) to explicitly configure each control. When you use this approach, the themed appearance will appear in Visual Studio.

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 StyleSheet designation indicates that this setting works more like CSS.) Here's an example:

<%@ Page Language="VB" AutoEventWireup="False" ... StyleSheetTheme="FunkyTheme" %>

Now the custom yellow background of the ListBox control takes precedence over the background color specified by the theme. Figure 12-12 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 like this, which is why it's often better to let your themes take complete control by using the Theme attribute.

Giving the control tag precedence over the theme

Figure 12.12. Giving the control tag precedence over the theme

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 attribute) and others are applied only if they aren't already specified in the control (those in the StyleSheetTheme attribute). However, in practice, this design is terribly confusing and not recommended.

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 on the web page 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 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.

ASP.NET is intelligent enough to catch it 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).

More Advanced Skins

So far, the theming examples have applied relatively simple properties. However, you could create much more detailed control tags in your skin file. Most control properties support theming. If a property can't be declared in a theme, you'll receive a build error when you attempt to launch your application.

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

Note

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 another one for all Cancel buttons. The first step to implement 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 this example, the images are stored in a folder named ButtonImages (see Figure 12-13).

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.

Adding images to a theme

Figure 12.13. Adding images to a theme

<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 App_ThemesThemeName 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 of a TreeView, the bullet image used for the BulletList control, or the icons used in a GridView.

Master Page Basics

The best websites don't look like a series of web pages—instead, they give the illusion of a continuously running application. For example, try ordering a book on Amazon. While you search, click through the links, and then head to your shopping cart, you'll always see a continuous user interface with a common header at the top, a set of navigation links on the left, and a footer at the bottom.

Creating something that polished with ASP.NET is possible, but it isn't as easy as it seems. For example, what if you want a navigation bar on every web page? Not only do you need to copy the same user interface markup to each page, you also need to make sure it ends up in the same place. An offset of a couple of pixels will completely ruin the illusion, making it obvious that the pages aren't really integrated. And even if you copy your markup perfectly, you're still left with an extremely brittle design. If you decide to update your navigation bar or change its position later, you'll need to modify every web page to apply the same change.

So how can you deal with the complexity of different pages that need to look and act the same? One option is to subdivide the page into frames. Frames are an HTML feature that lets the browser show more than one web page alongside another. Unfortunately, frames have problems of their own, including that each frame is treated as a separate document and requested separately by the browser. This makes it difficult to create code that communicates between frames. A better choice is to use ASP.NET's master pages feature, which allows you to define page templates and reuse them across your website.

Note

Frames are also out of favor because they limit your layout options. That's because each frame occupies a separate, fixed portion of a window. When you scroll one frame, the other frames remain fixed in place. To create frames that work properly, you need to make assumptions about the target device and its screen size. Most popular websites (think Google, Amazon, and eBay) don't use frames.

Master pages are similar to ordinary ASP.NET pages. Like ordinary pages, master pages are text files that can contain HTML, web controls, and code. However, master pages have a different file extension (.master instead of .aspx), and they can't be viewed directly by a browser. Instead, master pages must be used by other pages, which are known as content pages. Essentially, the master page defines the page structure and the common ingredients. The content pages adopt this structure and just fill it with the appropriate content.

For example, if a website such as http://www.amazon.com were created using ASP.NET, a single master page might define the layout for the entire site. Every page would use that master page, and as a result, every page would have the same basic organization and the same title, footer, and so on. However, each page would also insert its specific information, such as product descriptions, book reviews, or search results, into this template.

A Simple Master Page and Content Page

To see how this works, it helps to create a simple example. To create a master page in Visual Studio, select Website

A Simple Master Page and Content Page

When you create a new master page in Visual Studio, you start with a blank page that includes a ContentPlaceHolder control (see Figure 12-14). The ContentPlaceHolder is the portion of the master page that a content page can change. Or, to look at it another way, everything else that's set in the master page is unchangeable in a content page. If you add a header, that header appears in every content page. If you want to give the content page the opportunity to supply content in a specific section of the page, you need to add a ContentPlaceHolder.

A new master page

Figure 12.14. A new master page

When you first create a master page, you'll start with two ContentPlaceHolder controls. One is defined in the <head> section, which gives content pages the add page metadata, such as search keywords and style sheet links. The second, more important ContentPlaceHolder is defined in the <body> section, and represents the displayed content of the page. It appears on the page as a faintly outlined box. If you click inside it or hover over it, the name of ContentPlaceHolder appears in a tooltip, as shown in Figure 12-14.

To make this master page example more practical, try adding a header before the ContentPlaceHolder (using an <img> tag) and a footer after it (using some static text), as shown in Figure 12-15. You'll notice that the content area of the page looks very small, but this appearance is deceptive. The content section will expand to fit the content you place inside.

A simple master page with a header and footer

Figure 12.15. A simple master page with a header and footer

Now you're ready to create a content page based on this master page. To take this step, select Website

A simple master page with a header and footer
Creating a content page

Figure 12.16. Creating a content page

Now you'll see something a little more interesting. Your content page will have all the elements of the master page, but the elements will be shaded in gray, indicating that you can't select or change them in any way. However, you can add content or drag and drop new controls into the ContentPlaceHolder region to create a page like the one shown in Figure 12-17. In fact, this is the only editable portion of your page.

A simple content page at design time

Figure 12.17. A simple content page at design time

The ContentPlaceHolder section will expand or collapse to fit the content you place in it. If you've added volumes of text, the footer won't appear until the end. If you've included only a single line of text, you'll see something more compact, as in Figure 12-17. To get a clearer look at your web page, you can run it in the browser. Figure 12-18 shows the content page that's being designed in Figure 12-17.

A simple content page at runtime

Figure 12.18. A simple content page at runtime

The real magic starts when you create multiple pages that use the same master page. Now, each page will have the same header and footer, creating a seamless look across your entire website.

How Master Pages and Content Pages Are Connected

Now that you've seen a master page example, it's worth taking a look behind the scenes to see how you implement the master page.

When you create a master page, you're building something that looks much like an ordinary ASP.NET web form. The key difference is that although web forms start with the Page directive, a master page starts with a Master directive that specifies the same information. Here's the Master directive for the simple master page shown in the previous example:

<%@ Master Language="VB" CodeFile="SiteTemplate.master.vb"
    Inherits="SiteTemplate" %>

The ContentPlaceHolder is less interesting. You declare it like any ordinary control. Here's the complete code for the simple master page:

<%@ Master Language="VB" CodeFile="SiteTemplate.master.vb"
    Inherits="SiteTemplate" %>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Untitled Page</title>
</head>
<body>
    <form id="form1" runat="server">
        <img src="apress.jpg" /><br />
        <asp:ContentPlaceHolder id="ContentPlaceHolder1" runat="server">
        </asp:ContentPlaceHolder>
        <i>This is a simple footer.</i>
    </form>
</body>
</html>

Note

For simplicity's sake, this example doesn't include a ContentPlaceHolder in the <head> section. Although this ContentPlaceHolder isn't required (and it's isn't used in this example), Visual Studio adds it by default to all new master pages.

When you create a content page, ASP.NET links your page to the master page by adding an attribute to the Page directive. This attribute, named MasterPageFile, indicates the associated master page. Here's what it looks like:

<%@ Page Language="VB" MasterPageFile="~/SiteTemplate.master"
    AutoEventWireup="False" CodeFile="SimpleContentPage.aspx.vb"
    Inherits="SimpleContentPage" Title="Untitled Page" %>

Notice that the MasterPageFile attribute begins with the path ~/ to specify the root website folder. Using the ~/ syntax is better, because it indicates unambiguously where ASP.NET can find your master page.

Note

You can use the ~/ characters to create a root-relative path—a path that always starts from the root folder of your web application. This is a special syntax understood by ASP.NET and its server controls. You can't use this syntax with ordinary HTML. For example, this syntax won't work in an ordinary hyperlink that isn't a server control (such as the <a> tag).

The Page directive has another new attribute—Title. That's because the master page, as the outermost shell of the page, always defines the <head> section of the page with a default title. Remember, your content page can't modify anything that's in the master page. However, this is an obvious shortcoming with the title information, so to circumvent it ASP.NET adds the Title attribute, which you can set to override the title specified in the master page with something more appropriate.

The rest of the content page looks a little different from an ordinary web form. That's because the content page can't define anything that's already provided in the master page, including the <head> section, the root <html> element, the <body> element, and so on. In fact, the content page can do only one thing—it can supply a Content tag that corresponds to the ContentPlaceHolder in the master page. This is where you insert the content for this page. As a result, your content pages are a little bit simpler than ordinary web pages.

Here's the complete code for the simple content page, with a single line of text and two line breaks added:

<%@ Page Language="VB" MasterPageFile="~/SiteTemplate.master"
    AutoEventWireup="False" CodeFile="SimpleContentPage.aspx.vb"
    Inherits="SimpleContentPage" Title="Content Page" %>
<asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1"
  runat="Server">
    <br />
    Here's some new content!
    <br />
</asp:Content>

For ASP.NET to process this page successfully, the ContentPlaceHolderID attribute in the <Content> tag must match the ContentPlaceHolder specified in the master page exactly. This is how ASP.NET knows where it should insert your content in the master page template.

Tip

If a master page defines a ContentPlaceHolder but your content page doesn't define a corresponding Content control, you'll see a black box in its place when you design the page in Visual Studio. To add the required Content control, right-click that section of the page, and choose Create Custom Content.

You should realize one important fact by looking at the content page markup. Namely, the content from the master page (the address bar and the footer) isn't inserted into the content file. Instead, ASP.NET grabs these details from the master page when it processes the page. This has an important effect. It means that if you want to change the header or footer that's used in all your content pages, you need to change only one file—the master page. When you make this change, it will appear in all content pages automatically. In other words, master pages don't just let you reuse standard elements; they also make it easy to update these details later.

Tip

Now that you understand how to hook up master pages and child pages, you can easily take an existing page and modify it to use your master page. However, you'll need to remove some of the basic boilerplate tags, such as <html>, <head>, and <body>, and wrap all the content in one or more <Content> tags. Visual Studio won't add the Content control automatically except when you're creating a new content page from scratch.

A Master Page with Multiple Content Regions

Master pages aren't limited to one ContentPlaceHolder. Instead, you can insert as many as you need to give the client the ability to intersperse content in various places. All you need to do is add multiple ContentPlaceHolder controls and arrange them appropriately.

Figure 12-19 shows a master page that needs more careful consideration. It includes an initial ContentPlaceHolder where the user can insert content, and then a shaded box (created by a <div> tag) that contains a heading (OTHER LINKS) and a second ContentPlaceHolder. The idea here is that the page is split into two logical sections. In the content page, you won't need to worry about how to format each section or how to position the other links box. Instead, you simply supply content for each portion, and ASP.NET will insert it into the correct location in the master page.

A master page with two content regions

Figure 12.19. A master page with two content regions

Here's the code for the master page (with the style portion of the <div> tag omitted to save space):

<%@ Master Language="VB" CodeFile="MultipleContent.master.vb"
    Inherits="MultipleContent" %>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Untitled Page</title>
</head>
<body>
    <form id="form1" runat="server">
        <img src="apress.jpg" /><br />
        <asp:ContentPlaceHolder id="MainContent" runat="server">
        </asp:ContentPlaceHolder>
        <i>
            <div style="...">
                <b>OTHER LINKS</b>
                <br />
                <asp:ContentPlaceHolder id="OtherLinksContent" runat="server">
                </asp:ContentPlaceHolder>
            </div>
            This is a simple footer.
         </i>
</form>
</body>
</html>

Tip

The most underrated part of a master page is the line break, or <br /> tag. If you forget to include it, you can easily end up having child content run into your headings. To avoid this problem, make sure you add the necessary whitespace in your master page. Never rely on adding it in your content pages, because content pages may not insert the correct amount of space (or insert it in the correct place).

When you create a new content page based on this master page, Visual Studio will start you with one Content control for each ContentPlaceHolder in the master page, making your life easy. All you need to do is insert the appropriate information. Here's a slightly shortened example, with some of the text replaced with an ellipsis (...) to save space:

<%@ Page Language="VB" MasterPageFile="~/MultipleContent.master"
    AutoEventWireup="False" CodeFile="MultipleContentPage.aspx.vb"
    Inherits="MultipleContentPage" Title="Content Page" %>
<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="Server">
    This is the generic content for this page. Here you might provide some site
    specific text ... </asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="OtherLinksContent"
  runat="Server">
    Here's a <a href="http://www.prosetech.com">link</a>.<br />
    ...
</asp:Content>

Figure 12-20 shows the final result. Notice how the two content sections flow into their designated locations seamlessly.

Using the multiple content master page

Figure 12.20. Using the multiple content master page

Another important trick is at work in this example. The master page doesn't just define the structure of the web page; it also supplies some important style characteristics (such as a default font and background color) through the <div> tag. This is another handy trick to offload the formatting work to the master page, which allows you to maintain it and modify it much more easily.

Note

If you create a master page without any ContentPlaceHolder controls, content pages won't be able to supply any content at all, and they'll always show an exact copy of the master page.

Default Content

So far, you've seen master page examples with two types of content: fixed content and page-supplied content. However, in some cases your situation might not be as clear-cut. You might have some content that the content page may or may not want to replace. You can deal with this using default content.

Here's how it works: You create a master page and create a ContentPlaceHolder for the content that might change. Inside that tag, you place the appropriate HTML or web controls. (You can do this by hand using the .aspx markup or just by dragging and dropping controls into the ContentPlaceHolder.)

For example, here's a version of the simple header-and-footer master page shown earlier, with default content:

<%@ Master Language="VB" CodeFile="SiteTemplate.master.vb"
    Inherits="SiteTemplate" %>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Untitled Page</title>
</head>
<body>
    <form id="form1" runat="server">
        <img src="apress.jpg" /><br />
        <asp:ContentPlaceHolder id="ContentPlaceHolder1" runat="server">
         This is default content.<br />
        </asp:ContentPlaceHolder>
        <i>This is a simple footer.</i>
    </form>
</body>
</html>

So, what happens when you create a content page based on this master page? If you use Visual Studio, you won't see any change. That's because Visual Studio automatically creates a <Content> tag for each ContentPlaceHolder. When a content page includes a <Content> tag, it automatically overrides the default content.

However, something interesting happens if you delete the <Content> tag from the content page. Now when you run the page, you'll see the default content. In other words, default content appears only when the content page chooses not to specify any content for that placeholder.

You might wonder whether the content pages can use some of the default content or just edit it slightly. This isn't possible because the default content is stored only in the master page, not in the content page. As a result, you need to decide between using the default content as is or replacing it completely.

Tip

You don't need to delete the <Content> tag by hand. Instead, you can use the Visual Studio smart tag. First, click to select the content region in design view. Then, click the arrow that appears in the top-right corner of the content region to open the smart tag. Finally, choose Default to Master's Content (to remove the <Content> tag and use the default content) or Create Custom Content (to add the <Content> tag back).

Master Pages and Relative Paths

One quirk that can catch unsuspecting developers is the way that master pages handle relative paths. If all you're using is static text, this issue won't affect you. However, if you add <img> tags or any other HTML tag that points to another resource, problems can occur.

The problem shows up if you place the master page in a different directory from the content page that uses it. This is a recommended best practice for large websites. In fact, Microsoft encourages you to use a dedicated folder for storing all your master pages. However, if you're not suitably careful, this can cause problems when you use relative paths.

For example, imagine you put a master page in a subfolder named MasterPages and add the following <img> tag to the master page:

<img src="banner.jpg" />

Assuming the file MasterPagesanner.jpg exists, this appears to work fine. The image will even appear in the Visual Studio design environment. However, if you create a content page in another subfolder, the image path is interpreted relative to that folder. If the file doesn't exist there, you'll get a broken link instead of your graphic. Even worse, you could conceivably get the wrong graphic if another image has the same file name.

This problem occurs because the <img> tag is ordinary HTML. As a result, ASP.NET won't touch it. Unfortunately, when ASP.NET processes your content page, the relative path in this tag is no longer appropriate. The same problem occurs with <a> tags that provide relative links to other pages and with the <link> element that you can use to connect the master page to a style sheet.

To solve your problem, you could try to think ahead and write your URL relative to the content page where you want to use it. But this creates confusion and limits where your master page can be used. A better fix is to turn your <img> tag into a server-side control, in which case ASP.NET will fix the mistake:

<img src="banner.jpg" runat="server"/>

This works because ASP.NET uses this information to create an HtmlImage server control. This object is created after the Page object for the master page is instantiated. At this point, ASP.NET interprets all the paths relative to the location of the master page.

And as with all server-side controls, you can further clear things up by using the ~/ characters to create a root-relative path. Here's an example that clearly points to a picture in an Images folder in the root web application folder:

<img src="~/Images/banner.jpg" runat="server"/>

Remember, the ~/ syntax is understood only by ASP.NET controls, so you can't use this trick with an <img> tag that doesn't include the runat="server" attribute.

Advanced Master Pages

Using what you've learned, you can create and reuse master pages across your website. However, still more tricks and techniques can help you take master pages to the next level and make them that much more practical. In the following sections, you'll look at how CSS styles can help you organize your layout and how your content pages can interact with the master page class in code.

Style-Based Layouts

For the most part, HTML uses a flow-based layout. That means as more content is added, the page is reorganized and other content is bumped out of the way. This layout can make it difficult to get the result you want with master pages. For example, what happens if you craft the perfect layout, only to have the structure distorted by a huge block of information that's inserted into a <Content> tag?

Although you can't avoid this problem completely, there are two options to help you control the layout of a web page:

HTML tables:

Using an HTML table in your master page, you can break a portion of your page into columns and rows. You can then add a ContentPlaceHolder in a single cell, ensuring that the other content is aligned more or less the way you want.

CSS positioning:

Using CSS styles, you can divide your content into columns or regions, each of which is held in a separate <div> element. You then place a single ContentPlaceHolder in each <div>.

Although these two approaches are similar, the CSS standard is similar and more modern. It's the one you'll consider in the following example. (To see the equivalent example using HTML tables, refer to the downloadable samples for this chapter, which include both approaches.)

The basic concept is simple (and you've already seen it at work in some of the examples earlier in this book, like the two-panel GreetingCardMaker example from Chapter 6). First, you divide your content into multiple sections, each of which is a separately positioned column or box. In your markup, you wrap each section in a <div> element. Finally, you apply a different style to each <div>, and you use the properties of CSS to position it appropriately.

For a good example, consider a traditional web application with a header, content panel, and two side panels. Figure 12-21 shows how this structure is broken up into separate <div> elements, each of which uses a separate style in an external style sheet.

A style-based layout

Figure 12.21. A style-based layout

The trick to creating this sort of multiple-column layout is to use absolute positioning to place the columns. To first step to implanting this design is to create four styles, named Header, LeftPanel, RightPanel, and CenterPanel (all of which you should place in an external style sheet). Here's what you need for the Header style:

.Header
{
  position: absolute;
  top: 10px;
  left: 10px;
  height: 60px;
  text-align: center;
}

This creates an absolutely positioned box that sits close to the top of the web page, taking the full available width but limiting itself to just 60 pixels of height (any excess content is cut off).

The LeftPanel style is more interesting:

.LeftPanel
{
  position: absolute;
  top: 70px;
  left: 10px;
  width: 160px;
}

Here, the style rule creates a left column that's 160 pixels wide. The column is placed 70 pixels from the top of the page and 10 pixels from left edge. Because these coordinates are relative to the side of the browser window, it really doesn't matter how big or small that window is.

A similar style rule places the rightmost column:

.RightPanel
{
  position: absolute;
  top: 70px;
  right: 10px;
  width: 160px;
}

Like the left column, the right panel is 160 pixels. The difference is that it's positioned away from the right edge of the browser window (because the style sets the right property, not the left property).

The final style creates the content region. This style can't use a fixed size, because the exact width depends on how much space is left over after the two panels are inserted on the sides. This, in turn, depends on the current width of the browser window. Fortunately, there's an easy way to deal with this unknown—you simply set the left and right margins large enough to fit the left and right columns, and your content will flow neatly into the available space.

For example, the left panel is 160 pixels wide and is placed 10 pixels from the left edge of the browser window, which means you need a left margin of 170 pixels, plus a few pixels of extra breathing space to prevent content or borders from touching. Here's a suitable style rule:

.CenterPanel
{
  position: absolute;
  top: 70px;
  margin-left: 175px;
  margin-right: 180px;
}

Now, you can use the styles in the master web page:

<div class="Header">
</div>

<div class="LeftPanel">
</div>

<div class="CenterPanel">
</div>

<div class="RightPanel">
</div>

It's worth emphasizing that when you use this technique, the order of the <div> sections doesn't matter, because each one is placed at a precise region. (The only exception is if the sections overlap, in which case the later sections can overwrite the earlier ones.) And because these rules are in an external style sheet, it's easy to use them in multiple pages or to tweak them later without editing your page.

The final step is to insert and fixed content and the ContentPlaceHolder elements. Here's a master page that does exactly that:

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
    <link href="LayoutStyles.css" rel="stylesheet" type="text/css" />
</head>
<body>
    <form id="form1" runat="server">

    <div class="Header">
      <h1>My Header</h1>
    </div>

    <div class="LeftPanel">
      <asp:TreeView ID="TreeView1" runat="server" Width="150px">
        <Nodes>
          <asp:TreeNode Text="Root" Value="New Node">
            <asp:TreeNode Text="Page 1" Value="Page 1"></asp:TreeNode>
            <asp:TreeNode Text="Page 2" Value="Page 2"></asp:TreeNode>
          </asp:TreeNode>
        </Nodes>
      </asp:TreeView>
    </div>

    <div class="CenterPanel">
      <asp:ContentPlaceHolder id="MainContent" runat="server">
      </asp:ContentPlaceHolder>
    </div>

    <div class="RightPanel">
      <asp:ContentPlaceHolder id="AdditionalContent" runat="server">
      </asp:ContentPlaceHolder>
    </div>
</form>
</body>
</html>

This master page uses fixed text in the header and a fixed TreeView for navigation in the left panel. (In this example, the TreeView also has a few dummy nodes added, just so that it appears in the page. When using this design in a real website, you'd bind the TreeView to a site map, as described in Chapter 13.) This master page use a ContentPlaceHolder in the middle content region (for the main page content) and another one in the right-side panel (for additional page content, such as related topic links, author or summary details, an advertisement, and so on).

Figure 12-22 shows the resulting master page and a content page that uses the master page (both in Visual Studio). You'll notice that the different <div> elements have been given different background colors (using the corresponding style), which makes it easier to distinguish the separate regions when you're trying this example.

A master page (top) and a content page (bottom) using style-based layout

Figure 12.22. A master page (top) and a content page (bottom) using style-based layout

To convert this example into something more practical, just replace the hard-coded text and TreeView nodes in the master page with the actual header and navigation controls you really want. All the child pages will acquire these features automatically. This is the first step to defining a practical structure for your entire website. You'll also want to tweak the styles to have the formatting you want, and you'll probably want to add a bit of space around the content in each region using the padding properties.

Tip

To get some style sheet examples for basic multicolumn layouts, check out www.bluerobot.com/web/layouts. For more ambitious examples of styles at work, check out www.csszengarden.com. There you'll see a richly formatted page that can be completely reformatted and rearranged just by switching its style sheet. Best of all, the site includes more 200 sample style sheets that you can download and try yourself.

Code in a Master Page

In all the examples in this chapter, master pages have provided static layout. However, just like a web page, master pages also include a code portion that can respond to events in the page life cycle or the constituent controls. For example, you could respond to the Page.Load event to initialize a master page using code, or you could handle clicks in a set of navigation controls to direct a user to the right page.

Interacting with a Master Page Programmatically

A master control isn't limited to event handling code. It can also provide methods that the content page can trigger as needed or provide properties that the content page can set according to its needs. This allows the content page to interact with the master page.

For example, imagine you want to give the user the ability to collapse the cell with the navigation controls to have more room to see the page content. You don't want to implement this feature in the master page, because you want it to be available only on certain pages. However, the content page obviously can't implement this feature on its own, because it involves modifying a fixed portion of the master page. The solution is to create a way for the content page to interact with the master page so it can politely ask the master page to collapse or hide the navigation controls as needed.

One good way to implement this design is by adding a new property named ShowNavigationControls to the master page class. This property, when set to False, could then automatically hide the navigation controls. Here's the property you need to add to the master page class:

Public Property ShowNavigationControls() As Boolean
    Get
        Return TreeView1.Visible
    End Get
    Set (ByVal Value As Boolean)
        TreeView1.Visible = Value
    End Set
End Property

You should notice a few important facts about this property. First, it's public so that other classes (and therefore other pages) can access it. Second, it just wraps the Visible property in the TreeView control on the master page. Whatever value is passed to ShowNavigationControls is simply applied to TreeView.Visible. This is useful because ordinarily the TreeView.Visible property isn't directly accessible to the content page.

To access this page, the content page uses the built-in Page.Master property. This page always returns the linked object for the master page. However, you can't access the ShowNavigationControls property directly as Page.Master.ShowNavigationControls, because the Page.Master property uses the base MasterPage class, and doesn't know anything about the properties you've added to your derived master page class. To get access to the custom members you've added (like ShowNavigationControls), you need to cast the Page.Master object to the appropriate type.

Here's the button handling code for a content page that hides or shows the navigation controls depending on whether a Hide or Show button is clicked. In this example, the master page class is named TableMaster.

Protected Sub cmdHide_Click(ByVal sender As Object, _
  ByVal e As EventArgs) Handles cmdHide.Click
    Dim master As TableMaster = CType(Me.Master, TableMaster)
    master.ShowNavigationControls = False
End Sub

Protected Sub cmdShow_Click(ByVal sender As Object, _
  ByVal e As EventArgs) Handles cmdShow.Click
    Dim master As TableMaster = CType(Me.Master, TableMaster)
    master.ShowNavigationControls = True
End Sub

Figure 12-23 shows this content page in action.

A content page that interacts with its master page

Figure 12.23. A content page that interacts with its master page

Note that when you navigate from one page to another, all the web page objects are re-created. Even if you move to another content page that uses the same master page, ASP.NET creates a different instance of the master page object. As a result, the TreeView.Visible property of the navigation controls is reset to its default value (True) every time the user navigates to a new page. If this isn't the effect you want, you would need to store the setting somewhere else (such as in a cookie or in session state). Then you could write code in the master page that always checks the last saved value. Chapter 8 has more information about the ways you can store information in an ASP.NET website.

The Last Word

Building a professional web application involves much more than designing individual web pages. You also need the tools to integrate your web pages in a complete, unified website. In this chapter, you considered three ways to do exactly that. First, you considered CSS, which lets you apply consistent formatting to HTML elements and web controls alike. Then, you considered the ASP.NET themes features, which lets you effortlessly apply a group of property settings to a control. Finally, you learned to use master pages, which allow you to standardize the layout of your website. All these features make it easy to bring your pages together into a well-integrated, consistent web application.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.138.120.187