ASP.NET 2.0 CSS Friendly Control Adapters: The White Paper

Contents


Summary

Control adapters let you change the HTML markup produced by ASP.NET controls. Rather than having to invent a new control to generate the markup you want, in many cases you can simply write a small adapter for an existing ASP.NET control.

This white paper discusses control adapters in general and a sample set of adapters in particular. The samples demonstrate how you can adapt controls so they are easier to style with CSS. The adapters you create in the future may share this or some other purpose.

Introduction

ASP.NET supplements client-side HTML with a set of powerful server-side tags. For example, by adding a single ASP.NET Menu tag to a web page you can create a completely functional menu. Without this Menu tag you would typically have to write dozens, even hundreds, of HTML tags. In fact, if you look at the HTML produced by ASP.NET's Menu control you'll find it does produce hundreds of HTML tags. Obviously it's a huge boost in your productivity as an author when you can use a single Menu tag rather than having to invent and maintain hundreds of HTML tags.

Some authors, however, would prefer that different HTML markup was produced by ASP.NET controls like Menu. For example, it's becoming increasingly common to see menus implemented using nested <ul> tags rather than the <table> tags produced by Menu and TreeView controls in version 2.0 of the ASP.NET framework. From nested <ul> tags you can develop the so-called pure CSS menus, an approach that continues to be developed and refined.

This presents a wonderful opportunity to demonstrate the power and flexibility of ASP.NET's control adapters. There are plenty of great things about ASP.NET's Menu control that would be shame to have to recreate. After all, the Menu control gives us a great mechanism for converting hierarchical data from a database or XML file into submenus and leaf menu items. All we want to do is replace the logic used by the Menu to generate HTML for these submenus and leaves. That's what control adapters let you do. They replace just the chunk of logic devoted to figuring out the final HTML that the control produces in response to a request from a browser.

So, for example, an adapter can improve the Menu control so it renders <ul> tags rather than its usual <table> tags. All the rest of the Menu tag is retained.

You may be thinking, "That's fine but I need my web site to work even if the client visits it with an old browser, one in which those pure CSS menus don't work!" The architects of the ASP.NET framework considered that. Their solution was to allow you to stipulate which browsers should trigger the use of which adapters. So, it's easy to configure your web site to use the adapted controls only when it's visited by, say, modern versions of particular browsers like Internet Explorer, Firefox or Opera.

Readers who are familiar with traditional object oriented programming may be thinking, "I can do the same thing by deriving a new class based on Menu and simply override the necessary rendering methods." That is certainly a viable approach but, in practice, adapters are easier to introduce to a site. You don't need to add the @Register directives to your pages (as you do when you use custom controls). Further, as mentioned above, the ASP.NET framework comes with a built-in means to apply the adapters to just certain browsers. When you build entirely new custom controls you have to write your own logic to handle browser detection to vary the response. That's not to say that custom controls are bad. Rather, it suggests that it's worth considering if an easy-to-write adapter might solve certain problems where you've employed custom controls in the past.

Background

Architecture

Developers interested in studying the theory and architecture underpinning control adapters are encouraged to review the previously published literature on the subject.

More details are revealed in the documentation provided by Microsoft for the most important classes related to adapters:

Practical Patterns

Along with understanding the architecture underpinning control adapters, you may wish to take a look at the ASP.NET QuickStart tutorials that discuss building adapters.

It's easy to create an ASP.NET control adapter.

  • Begin by creating a class derived from System.Web.UI.Adapters.ControlAdapter or a class derived from it, e.g., System.Web.UI.WebControls.Adapters.DataBoundControlAdapter or System.Web.UI.WebControls.Adapters.HierarchicalDataBoundControlAdapter. Override a few methods like RenderBeginTag, RenderContents and RenderEndTag that produce the adapted control's markup.

    This sample control adapter kit demonstrates how this is done. For example, the screenshot below shows a portion of this kit's TreeView adapter code (C# version).

    TreeView adapter code

    The adapter class that you create can be added into your web project and compiled as part of the project. For even greater re-use, you could add it to a class library project and reference it from multiple web projects.

  • To register the adapter class add an entry to a .browser file in the App_Browsers folder. See this kit's tutorial for more information about the .browser file.

View State

View state can be characterized in a weird and funny way: when it works well you don't notice it!

The term view state refers to the overall appearance of a web page just before its <form method="post"> is submitted by the user. A page maintains its view state when it restores its appearance after posting back to the server.

When it works well you don't notice it. The reloaded page simply looks like it did a moment earlier. Typically, one only notices failures to maintain view state: places where the page looks different after it reloads.

A textbox can restore its appearance merely by setting the value attribute in its <input> tag to whatever was submitted. Maintaining the view state of a tree or other complex control can be far more complicated.

The traditional ASP.NET TreeView renders itself as an elaborately nested set of <table> tags. This kit's TreeView adapter renders nested <ul> tags. Either way, the tree uses client-side JavaScript to allow users to expand a node to reveal its children. The server typically has no idea this is happening. So, on the client, the tree must create a map of its expansion state and send it to the server when the page is submitted if it hopes to restore that state.

There are a variety of ways to send that data back to the server. Often a hidden form element is used. This is the strategy employed by the TreeView adapter in this kit.

Here is an outline of the implementation pattern used by the CSSFriendly.TreeViewAdapter class to maintain view state.

  • Declare a private HiddenField property called _viewState.
  • Override the SaveAdapterViewState, LoadAdapterViewState and OnLoad methods.
  • In SaveAdapterViewState, register some JavaScript that sets the value of a hidden form element. The value will be a string representing which nodes in the tree are expanded immediately before the form is submitted. SaveAdapterViewState itself returns the same sort of string representing how the tree is expanded at present.
  • In LoadAdapterViewState, find the value submitted in the hidden form element. Compare it to the original value to see if the view state has changed. Note if they differ.
  • In OnLoad, use the latest view state data retrieved in LoadAdapterViewState to re-expand the tree.

Samples

Goals and Scope

First and foremost, the samples included with this web site are intended to demonstrate how to develop and use control adapters. You may find these samples to be sufficiently robust for use in your own ASP.NET 2.0 web sites. Or, you may wish to enhance these samples before deploying them. In some cases you'll find that they serve as a pattern from which to create entirely new adapters for other ASP.NET controls.

Each of the sample adapters recognize a limited number of the public properties of the control it adapts. These samples produce markup that can be easily styled with classic CSS techniques. Therefore, these adapters ignore control properties like Font or BorderColor. Without the adapters, those sorts of properties cause the control to generate additional markup, inline or embedded CSS. When the sample adapters are enabled, these sorts of stylistic control properties are not needed since these same effects can be achieved by creating your own CSS rules to control the choice of font or border color.

The properties that the sample control adapters honor are properties that impact content, not style. For example, if you specify the HeaderText property on a DetailsView and FormView control, it will be recognized as important by the adapter because it impacts what markup and content is produced, rather than what that content looks like.

Remember that these adapters are samples. Since they are not part of the official ASP.NET framework you may find a few control properties (unrelated to style) that the adapters ignore. Keep in mind that the intent of these adapters is to show you how to build adapters. Armed with this knowledge you'll, hopefully, discover that it's fairly straightforward to enhance the adapters to support additional control properties.

Architecture

These sample control adapters produce markup that is intended to be simple and predictable. For example, the adapters for the Menu and TreeView controls produce nested <ul> tags that mimic the nesting of the hierarchical data bound to that Menu or TreeView.

Similarly, each adapter predictably assigns CSS classes to some of the tags it produces.

By keeping this scheme simple it's fairly easy to create a set of CSS rules that completely govern the appearance of the adapted control.

This is a departure from the approach historically taken by the ASP.NET controls where you never need to know anything about the markup ultimately produced by a control. In the past, when you've needed to alter the appearance of a control you modified the value of one of its properties.

For example, when building a Menu with a blue background you would traditionally set a property like StaticMenuStyle-BackColor. When the adapters are used, you'll instead modify a CSS rule like this:

CSS
1
2
3
4
5
        ul.AspNet-Menu li
        {
            background:Blue;
        }

CssSelectorClass

It's quite common for a page to contain more than a single Menu or TreeView, etc. In those cases you'll typically want each control to look distinct. For example, a page might have three menus: two of which must look similar, the third must look different. Fortunately, these sample control adapters make it easy accomplish this.

The key is to use a new (expando) attribute called CssSelectorClass. For instance, the Menu example contains two Menu tags that look like this:

ASP.NET
1
2
3
4
5
        <asp:Menu ID="Menu1" runat="server" SkinID="SampleMenuVertical" DataSourceID="ExampleSiteMapDS" 
        CssSelectorClass="PrettyMenu" />
        <asp:Menu ID="Menu2" runat="server" SkinID="SampleMenuHorizontal" DataSourceID="ExampleSiteMapDS" 
        CssSelectorClass="PrettyMenu" Orientation="Horizontal" />

Notice that CssSelectorClass has a value of PrettyMenu. Now look at the CSS selectors found in MenuExample.css. (This is the style sheet that controls most aspects of how the menus appear.) Many of the rules include PrettyMenu in their selectors. For example:

CSS
1
2
3
4
5
6
        .PrettyMenu ul.AspNet-Menu li:hover, 
        .PrettyMenu ul.AspNet-Menu li.AspNet-Menu-Hover
        {
            background:#4682B3;
        }

This CSS rule controls the background color that the menu should use to highlight an item that the cursor is hovering over.

Now, return to the Menu sample. Notice that there is a third menu. It's at the top of the page and lets you navigate to the other example pages. It uses a completely different set of colors, etc., compared to the menus demonstrated in the center of the page. This menu (at the top of the page) is created in Main.master like this:

ASP.NET
1
2
        <asp:Menu ID="MainNav" runat="server" SkinId="MainNav" DataSourceID="SiteMapDS" CssSelectorClass="MainMenu" Orientation="Horizontal" />

Notice that the CssSelectorClass has a different value in this case. It is set to MainMenu. Now look at the CSS selectors in MainMaster.css. Notice that the rules pertaining to this menu are distinguished by their use of MainMenu. For example:

CSS
1
2
3
4
5
6
7
        .MainMenu .AspNet-Menu-Horizontal ul.AspNet-Menu li:hover,
        .MainMenu .AspNet-Menu-Horizontal ul.AspNet-Menu li.AspNet-Menu-Hover
        {
            color: White;
            background: #165EA9 url(bg-menu-main.png) repeat-x;
        }

The different CSS classes (PrettyMenu and MainMenu) allow us to create CSS rules that visually distinguish one group of menus from another.

All of the sample control adapters (not just Menu) allow you to use the expando property: CssSelectorClass.

Conventions

When you first learn to use CSS you tend to build fairly simple rules that govern straightforward properties like color, font size or border thickness. Later, you discover that you can use CSS to precisely control the position of elements on a page with properties like padding, margin or position. Ultimately, you find that by coupling certain properties (like display and float) to pseudo-classes (like hover), you can control the behavior of web elements. In fact, these behavioral techniques are the crux of implementations like pure CSS menus.

In practice, you rarely need to change behavioral CSS rules when you reskin a web site. Instead, you tend to find it is sufficient to modify the CSS rules governing simple display properties like color. Therefore, the CSS rules that apply to these sample web pages are segregated into two groups:

  • Rules that primarily control simple properties like color.
  • Rules that primarily control behavior like when an element should appear or disappear.

In these samples, behavioral CSS rules are contained in style sheets named for the controls they pertain to. You will probably not need to edit these rules, even when you reskin a site or use these sample adapters in your own web sites. The style sheets containing these behavioral rules are found in the CSS folder.

CSS rules pertaining to properties like color are contained in style sheets in the individual theme subfolders beneath the App_Themes folder.

Unfortunately, the distinction between behavioral CSS rules and design-centric rules is a bit fuzzy. For example you might imagine that you could modify any rule in MenuExample.css (or even omit MenuExample.css from your web site entirely) without compromising the behavior of a menu. Alas, that isn't true. It is possible to alter certain CSS properties that are generally considered design-centric in such a way that they end up radically altering the behavior of a control like Menu. This is particularly true of CSS properties like margin, padding and (especially) width. Small changes to these sorts of properties are typically made to alter the look of a web page by slightly shifting content left/right or up/down. However, you can sometimes stumble into situations where an element becomes too wide to be properly contained by its parent element (which may have a smaller width) so its placement on the page suddenly is radically altered. It may, for example, have to be pushed below other elements in order for the browser to find room for it. When these sorts of radical layout changes occur on a page the overall behavior of something like a Menu can be unintentionally damaged.

In practice, when modifying properties like margin, padding and width you'll find that it's usually best to make a small handful of changes to the CSS rules and then test the results by refreshing the page in a browser. Typically, if your changes suddenly push some property past its limit you'll see an obvious problem on the page. If this occurs, you can back out the CSS changes you made and try again with smaller changes to the margin, padding and width.

Adapted Controls

The following diagrams show some of the more important styles used in the example pages. By studying these images you will better appreciate the influence of these styles on the rendition of each control.

Menu

CSS rules that impact the markup rendered by the Menu adapter

TreeView

CSS rules that impact the markup rendered by the TreeView adapter

DetailsView

CSS rules that impact the markup rendered by the DetailsView adapter

FormView

CSS rules that impact the markup rendered by the FormView adapter

GridView

CSS rules that impact the markup rendered by the GridView adapter

DataList

CSS rules that impact the markup rendered by the DataList adapter

Login

CSS rules that impact the markup rendered by the Login adapter

ChangePassword

CSS rules that impact the markup rendered by the ChangePassword adapter

PasswordRecovery

CSS rules that impact the markup rendered by the PasswordRecovery adapter

CreateUserWizard

CSS rules that impact the markup rendered by the CreateUserWizard adapter

LoginStatus

CSS rules that impact the markup rendered by the LoginStatus adapter

Using These Adapters in Your Web Site

These control adapters can be used in any ASP.NET 2.0 web site, not just this sample site. When you download and install this kit's VSI file new web site templates will be added to Visual Studio. These allow you to reproduce this whole sample kit locally or create a slimmer version that has just the essential files plus a small number of sample pages to get you started.

If you want to integrate these adapters into an existing site, follow these instructions:

Copy Some Files

  • Locate the root folder for your web application. Create the following subfolders in this root folder if they don't yet exist:
    • App_Browsers
    • App_Code
    • App_Themes
    • CSS
    • JavaScript
  • Copy the file named CSSFriendlyAdapters.browser from the App_Browsers folder in this sample web application to your web application's App_Browsers folder. Typically you won't need to edit this file; its current settings will work for your web application, too. You can define which browsers use the adapters by editing CSSFriendlyAdapters.browser. When developing your own sites in the future, it's helpful to remember that you can experimentally delete CSSFriendlyAdapters.browser when you want to see the unadapted markup that the ASP.NET framework would normally generate for a page.
  • Copy the entire Adapters folder from the App_Code folder in this sample web application to your web application's App_Code folder. You do not need to modify these files, nor do you need to compile them or build a DLL explicitly from them. ASP.NET 2.0 will do all that work automatically for you. (Of course, you can build a DLL if you prefer. In that case, you would put that DLL into your web application's bin folder and you would not copy the Adapters folder to App_Code.)
  • Copy the entire JavaScript folder in this sample web application to your web application. You do not need to modify these files.
  • Copy the entire CSS folder in this sample web application to your web application. Be sure to copy the BrowserSpecific subfolder, too. The following markup must be present in your page's <head>. It imports a set of style sheets with rules that are independent of your page's theme. Conditional comments are used to add a special style sheet used only for previous versions of Internet Explorer. See Main.master for an example of where this markup is used in this sample kit's pages.
    CSS
    1
    2
    3
    4
    5
    
    <link runat="server" rel="stylesheet" href="~/CSS/Import.css" type="text/css" id="AdaptersInvariantImportCSS" />
    <!--[if lt IE 7]>
        <link runat="server" rel="stylesheet" href="~/CSS/BrowserSpecific/IEMenu6.css" type="text/css">
    <![endif]-->
    
  • Create a folder (or choose an existing folder) within the App_Themes folder. This is your theme folder.
  • Copy some or all of the files from this sample web application's Basic theme folder into your theme folder. For example, if you want to create an adapted Menu, the styles in MenuExample.css will get you started. Copy that file into your own theme folder (a subfolder of App_Themes in your web site). Then, modify the CSS property values and perhaps the CSS selectors for the rules to make Menu appear as you wish. Making CSS property modifications is discussed in more detail later in this document. See: Match CSS to ASP.NET Attributes.
  • Depending on if/how you modify the CSS rules in the various [control]Example.css files you may need some of the images found in this sample web application's Basic theme folder. Likewise you may wish to create an Images folder in the root of your web application and copy into it some (or all) of the files found in this sample web application's Images folder.
  • This sample web site's CSSFriendlyAdapters.browser file tells the web site to always use the CSS friendly control adapters. You can, however, configure it to use the adapters for specific versions of specific browsers only. If you do so be sure to specify how you want your controls rendered when the adapters aren't used. Typically, this is done by adding a skin file to a theme folder. This kit includes sample skin files like Basic.skin.

Link to Style Sheets

CSS rules are typically stored in style sheet files. If you used early versions of ASP.NET then you're probably used to adding style sheets to your pages manually by adding <link> tags to the <head> section of the page. The latest version (2.0) of ASP.NET introduces themes which are really just subfolders under the App_Themes folder where you put CSS files, skin files, and images. Each page can specify which theme it uses. ASP.NET 2.0 automatically links to the CSS files in that theme folder.

If you use the theme system in ASP.NET 2.0 you simply drop your CSS files into the proper theme folder and that CSS file will be linked into any page that uses that theme. That is why the instructions above say to put certain CSS files into a folder under App_Themes. It is so you could use that theme in your web site's pages and automatically have those pages link in those CSS files.

For example, in this sample web site the file App_Themes\Basic\FormViewExample.css is automatically linked into all the example pages like FormView.aspx where the @Page page includes the attribute StyleSheetTheme="Basic".

You can use ASP.NET themes to link to your CSS files or create those links manually. Either way, you need to create CSS rules in these style sheets to govern how your adapted Menus, TreeViews, etc., will look. These CSS rules will define the sorts of properties that you've traditionally defined as attributes in tags like <asp:FormView>.

Match CSS to ASP.NET Attributes

Amongst the instructions listed above, the crux is putting the CSSFriendlyAdapters.browser file into the App_Browsers folder. We're using this browser file to tell our web site to adapt the rendition of some controls like this:

CSS
1
2
3
    <adapter controlType="System.Web.UI.WebControls.FormView"
           adapterType="CSSFriendly.FormViewAdapter" />

Now when the web site executes a page that contains an <asp:FormView> (whose fully qualified class name is System.Web.UI.WebControls.FormView) it will automatically:

  • Create an instance of the CSSFriendly.FormViewAdapter class.
  • Call that adapter's various Render* methods whenever it needs to obtain the HTML that represents that particular FormView instance.

You don't have to create your adapter instances or call their Render* methods explicitly. The ASP.NET framework does that for you automatically once you've added the browser file to your web site's App_Browsers folder.

Remember, these sample adapters generally ignore style-related attributes. Imagine your page contains a FormView like this:

CSS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
    <asp:FormView

        ID="FormView1"
        Runat="server"
        DataSourceID="ContactsDS"
        HeaderText="Author Details"
        AllowPaging="True" 

        backcolor="White" 
        borderstyle="None" 
        gridlines="None" 
        cellspacing="2"
        HeaderStyle-forecolor="#F7F6F3"
        HeaderStyle-backcolor="#5D7B9D"
        HeaderStyle-font-bold="True"
        RowStyle-forecolor="#333333"
        RowStyle-backcolor="White"
        PagerStyle-forecolor="#00FFFF"
        PagerStyle-horizontalalign="Center"
        PagerStyle-backcolor="#284775" />

In this example FormView tag, the first set of attributes relates to content; the second set relates to style.

When the adapters are not used, this <asp:FormView> tag will create HTML with lots of embedded attributes to specify colors, etc. The HTML created by the FormView adapter, however, is utterly different. It doesn't include specifics about colors, etc. Instead, these properties are defined in CSS files.

In other words, when the CSS friendly FormView adapter is used, the specific FormView example shown above will render the same HTML as this simpler one:

CSS
1
2
3
4
5
6
7
8
    <asp:FormView
        ID="FormView1"
        Runat="server"
        DataSourceID="ContactsDS"
        HeaderText="Author Details"
        AllowPaging="True"
    />

Attributes like DataSourceID, HeaderText and AllowPaging tell the FormView what content to display. They do not tell the FormView how to display that content (e.g., its color, font, borders, etc.). The CSS friendly adapters pay attention to content-related attributes but ignore attributes related to styling.

So, after you've added the adapter files to your web site, your job will be to create CSS rules that match the ASP.NET style-related attributes. Here is an example of how this was done for the FormView example.

<asp:FormView> attribute Matching CSS rule for the adapted FormView Notes
backcolor="White" .AspNet-FormView { background-color: White; }  
borderstyle="None"   No comparable CSS because the adapted FormView doesn't use tables so there are no borders shown by default.
gridlines="None"   No comparable CSS because the adapted FormView doesn't use tables so there are no grid lines shown by default.
cellspacing="2" .AspNet-FormView-Data { padding:7px 0 0 4px; } These padding values result in white space in a manner similar to using ASP.NET's cellpadding attribute, though the two aren't 100% equivalent. The point of making your CSS rules isn't always to match the adapted and unadapted control renditions pixel for pixel. You are simply trying to create CSS that produces an adapted rendition that is comparable and pleasing.
HeaderStyle-forecolor="#F7F6F3" .AspNet-FormView-Header { color: #F7F6F3; }  
HeaderStyle-backcolor="#5D7B9D" .AspNet-FormView-Header { background-color: #5D7B9D; }  
HeaderStyle-font-bold="True" .AspNet-FormView-Header { font-weight: bold; }  
RowStyle-forecolor="#333333" #SampleFormView .Sample-Phone { color: #333333; } Sometimes you'll find it convenient to invent classes that you then use explicitly within tags found inside your FormView's <ItemTemplate>.
RowStyle-backcolor="White"   No comparable CSS because the adapted FormView doesn't use tables so the background is inherited from parent objects like the <body>, which already have a White background.
PagerStyle-forecolor="#00FFFF" .AspNet-FormView-OtherPage { color: #00FFFF; }  
PagerStyle-horizontalalign="Center" .AspNet-FormView-Pagination { text-align:center; }  
PagerStyle-backcolor="#284775" .AspNet-FormView-Pagination { background-color: #284775; }  

Matching ASP.NET style-related attributes to comparable CSS attributes is the bulk of the work you'll need to do in order to use these CSS friendly adapters. You'll put these rules into a file like FormViewExample.css that you ensure is linked to your page (either manually via a <link> tag in the <head> of your page or by using an ASP.NET theme folder). By the way, there is nothing particularly special about this file name: FormViewExample.css. You can name this sort of style sheet file anything you prefer.

See the CssSelectorClass section of this document for more details regarding the leading class name (like PrettyFormView) found in many of the example CSS rules. You'll almost certainly want to choose CssSelectorClass names that suit your web pages better.

Test Your Work

Once you get your CSS rules written and put your adapter files, etc., in the right locations in your web site, you are ready to test the result. This is no more complicated than viewing your pages in various browsers: Internet Explorer (versions 5, 6 and/or 7), Firefox, Opera, Safari, etc.

Remember that the adapters will be used based on whatever elements are in your browser file in the App_Browsers folder. Only the browsers mentioned in that browser file will cause the adapters to be used. All other browsers will render the controls using the traditional ASP.NET mechanisms.

The sample browser file included with this kit tells the framework to use the adapters by default. If you modify that be sure to provide the appropriate ASP.NET style-related attributes (in the tag itself, on the page or in a skin file) so your page continues to look correct if it is visited by a browser for which the adapters are not used.

A simple but powerful testing strategy is to temporarily delete the browser file(s) from the App_Browsers folder. Then visit your web pages again. This is what they will look like when visited by browsers that are not configured to use the CSS friendly adapters.

Accessibility

Accesskey

Over the years, authors have suggested many ways to use the HTML accesskey attribute to make pages more accessible and easier to use. An accesskey is a keyboard shortcut. It can move the focus to a particular form element. Or, it can be the equivalent of clicking a button or link.

When using Windows, you invoke an accesskey by holding down the Alt key and whatever letter or number is assigned to the accesskey attribute. On a Macintosh you use the Ctrl key. Linux users use the Meta key. On Windows systems, you can sometimes resolve conflicts by using the Shift and Alt keys together with the accesskey character.

Some authors advocate consistently using a first letter heuristic for accesskey values. This pattern can be implemented in menus or other complex clusters of hyperlinks.

In theory, all this sounds great. In reality things aren't so simple. The biggest problem is that accesskey settings sometimes coincide and conflict with existing keyboard shortcuts used by the browser or parts of the operating system. Even within a single web page you need to take care not to use the same accesskey value for two different HTML elements. Menus can easily contain dozens of items. If the text for any two menu items starts with the same letter then you will have an accesskey conflict if you use the first letter pattern. For this reason, the sample Menu and TreeView adapters found in this kit do not try to set the accesskey attribute.

There are particular dangers when using accesskey values for hyperlinks or form buttons. These take immediate actions: navigate to another page, submit a form, etc. Confusion can easily result if the user presses a key combination resulting in immediate and unexpected action like submitting an incomplete form.

An accesskey on a textbox, radio button or checkbox typically causes no dramatic action to be taken. For example, the accesskey for a textbox merely moves the cursor to within it. Likewise, the accesskey for a radio button or checkbox simply toggles its state.

This kit's membership control adapters programmatically set the accesskey values for the textboxes and checkboxes. The first letter within the associated label is emphasized to give a visual cue. So the rendered HTML looks like this:

ASP.NET
1
2
        <label for="someTextbox"><em>M</em>y label text:</label>

Then, it is a simple matter of establishing a CSS rule that governs how an <em> within a <label> should be displayed.

CSS
1
2
3
4
5
6
        .PrettyCreateUserWizard .AspNet-CreateUserWizard label em
        {
            text-decoration: underline;
            font-style: normal;
        }

To see this in action, go to the CreateUserWizard sample. Navigate with accesskeys. If using Windows, press Alt + e to set the focus to the textbox for the Email address. (Prior to Internet Explorer 7 you must also hold down the Shift key to use the password textbox's accesskey.)

There are two ways to stop the automatic generation of accesskey values:

  • Set the (new expando) attribute AutoAccessKey to false on the membership control tag in your markup.
  • Modify the implementation of the AutoAccessKey property in the WebControlAdapterExtender class.

Scalable Styling

Scalable styling is a good way to make your web site more accessible.

All of this kit's adapters produce markup that fundamentally scales well. The CSS that you use to style that markup, however, may or may not handle scaling. It requires some measure of experience and discipline to consistently use proportional CSS units like em or percent rather than pixels and other so-called fixed units. The sample CSS included with this kit is intended to look great and act well if you increase the browser font size by one notch. Scaling more generally results in a reasonable presentation, though you may notice some anomalies.

To see this in action, go to the Menu sample. Increase your browser's font size. In Internet Explorer use View > Text Size > Larger.

Standards

The markup rendered by this kit's sample adapters is designed to work on pages adhering to the XHTML 1.1 Strict or earlier standards.

The markup found within this kit's pages is also XHTML 1.1 Strict. You can test these or your own public pages with the markup validator provided by the W3C organization.

Quick demo: Validate the Menu sample page to confirm that the page conforms to the XHTML 1.1 Strict standard.

Generally these sample adapters only need to carefully choose the tags and attributes they render in order to produce markup that conforms to the XHTML 1.1 Strict standard. There are exceptions, however. Most notably, when the server-side attribute named Target is set to the value _blank the adapters do not render a simple target attribute in the HTML. Rather, they use the onclick and onkeypress attributes. This achieves the same purpose as setting target to _blank in the HTML but is XHTML 1.1 Strict compliant.

Special TreeView Notes

OnClientClickedCheckbox

When using the adapter, you may optionally include the new OnClientClickedCheckbox attribute on the <asp:TreeView> tag to provide a JavaScript statement to execute on the client whenever one of the tree's checkboxes is changed. This kit includes an example of using OnClientClickedCheckbox to allow checkmarks to cascade without a postback to the server.

Adapted Events

To handle the TreeView's SelectedNodeChanged or TreeNodeCheckChanged events you must use a new attribute called OnAdapted[eventname]. Set that attribute to the name of a public (not protected) method of the page that contains the TreeView.

No special attributes or handling is needed for the TreeNodePopulate event.

C#
1
2
3
4
5
6
7
<script runat="server">
    public void OnClick(Object sender, EventArgs e)
    {
        // do something with foobar.SelectedNode
    }
</script>
Visual Basic
1
2
3
4
5
6
<script runat="server">
    Public Sub OnClick(ByVal sender As Object, ByVal e As EventArgs)
        ' do something with foobar.SelectedNode
    End Sub
</script>
ASP.NET
1
2
<asp:TreeView ID="foobar" runat="server" OnSelectedNodeChanged="OnClick" OnAdaptedSelectedNodeChanged="OnClick" />

Disabling Adapters

If you explicitly add AdapterEnabled="false" to your server-side tag, these sample adapters will attempt to use the ASP.NET framework's native rendering for the control. Beware: this is not supported and often does not work well. Fundamentally, the framework does not support disabling adapters on a per control basis. The AdapterEnabled attribute is only intended to be used experimentally.

Hidden Nuggets

Syntax Highlighter

This sample web site includes a source code viewer that employs various text colors to highlight language keywords, etc. The logic behind how to colorize and display the source code text is entirely encapsulated in a custom control generously contributed to the ASP.NET community (and used herein with permission) by Wilco Bauwer.

Source Code Viewer

The very first SDK for ASP.NET (v1.0) came with a source code viewer that various authors have integrated into their own web sites. Rather than recycling this somewhat aging technology, a new source code viewer was developed for this site by combining Wilco Bauwer's syntax highlighter with a TreeView control.

With just a few simple tags and a few lines of code we end up with a powerful and effective source code viewer. You are, of course, welcome to use this approach in your own web sites.

Conclusion

Copy, modify and improve the markup, CSS and code demonstrated and documented here. Or, invent entirely new adapters for a completely different set of ASP.NET controls.

Control adapters let you take charge of your web site's markup and design in a totally new way. Their potential to shape and change the way we look at web site architecture is enormous and completely untapped. Truly, this is a frontier, an area where clever web designers and developers can push the boundaries of what we thought possible.

We're looking forward to hearing from you. Please post any comments, questions, or bug reports to the CSS Adapters Forum at http://forums.asp.net/1018/ShowForum.aspx.