img waterboy

Accessibility Improvements in ASP.NET 2.0

发表于2004/9/19 9:59:00  1242人阅读


Table of Contents

  • Page 1
    • Introduction
    • Accessibility and the Law
    • Accessibility Issues in Web Pages
      • Areas for Improvement
    • Recommendations for Maximizing Accessibility
      • Assisting Navigation
      • Identifying Elements and Accessing Alternate Content
      • General Recommendations
      • CSS Style Sheets
      • Testing Your Sites and Applications

  • Page 2
    • Server Control Accessibility Enhancements in ASP.NET 2.0
      • Example 1: Accessibility with HtmlLink and GridView Controls
        • Linking a CSS Stylesheet with the New HtmlLink Control
        • Connecting a Label Control and Specifying a HotKey

  • Page 3
    • Captions, scope, and headers Attributes in the GridView Control
      • Adding a Table Caption
      • Specifying Meaningful Column Names
      • Specifying the Row Header in a GridView Control
      • Viewing the Results in the IBM Home Page Reader
    • About the Author

  • download sample code


Some two years ago, I was honored to be part of the design review panel for ASP.NET, looking at the proposed feature set for version 2.0. That seems a long time ago now, but one of concerns I expressed at the time was that ASP.NET should provide better support for developers to add features to their pages that improve accessibility for all users. The World Wide Web Consortium (W3C) and many other industry bodies have long been pushing for wider support across all sites for disabled and disadvantaged users, many of whom use special types of user agent to access Web pages.

The W3C issues guidelines under the umbrella of the Web Accessibility Initiative (WAI), whose mission is "...to lead the Web to its full potential..." includes amongst its goals "... promoting a high degree of usability for people with disabilities" (see http://www.w3.org/WAI/ for more details). Other sites provide useful guides, software, and tools that make it easier to build and test pages that maximize accessibility. You only have to search the Web for "Web Accessibility Guidelines" to get some idea of the variety and number of sites devoted to this topic.

However, this article is not a general reference to accessibility issues or a primer on building accessible pages. What it does cover is how the ASP.NET team at Microsoft has added new features to the Framework that you can take advantage of in your Web applications to more easily provide better support for alternative types of browsers and user agents. It involves little extra effort on the part of the developer but can make the world of a difference to users.

Accessibility and the Law

While the issues of providing reasonable accessibility for as large a proportion of the population as possible might seem to be justifiable on their own, you also have to consider the legal ramifications of not following such a policy. As in most walks of life, governments are unwilling to leave it to industry to manage their own affairs. To some extent, this is understandable - the W3C accessibility initiatives have been around for a while, yet recent surveys show that only a small number of "large commercial Web sites" (around 20%) have made any real attempts at implementing them.

In the US, the accepted standards are based on the Section 508 Standards for Electronic and Information Technology (see http://www.access-board.gov/sec508/guide/). These were originally aimed at companies providing products or services to government departments and public bodies, stating that: "Any organization or company that contracts with the Federal government must ensure that its Web site and electronic data are available to the public in a manner that is accessible to people with disabilities." The meaning of the phrase "contracts with" is becoming more tenuous as time goes by...

In Europe, in June 2002, the eEurope Action Plan was adopted by the Feira European Council. One of the specific targets of this plan is to improve access to the Web for people with disabilities and, of course, to adopt and implement the results of the Web Accessibility Initiative project. A statement from the council says that: "In Europe, we have now clearly established that actions must be taken to identify and remove these [accessibility] barriers. The European Commission is fully committed to this goal."

In the United Kingdom, a 1999 Act of Parliament, The Disability Rights Commission Act is in force, and in 2003 the commission began a formal (and ongoing) investigation into Web site accessibility for disabled people. It is currently carrying out "... a systematic evaluation of the extent to which the current design of websites ... facilitates or hinders use by disabled people in England, Scotland and Wales." It is also performing "...analysis of the reasons for any recurrent barriers identified by the evaluation ..." and producing "... recommendations for further work which will contribute towards enabling disabled people to enjoy full access to, and use of, the Web."

Probably the most direct and unequivocal indication of the way governments around the world are beginning to involve themselves in accessibility issues is from Australia. Dr. Sev Ozdowski, the acting Disability Discrimination Commissioner at the time, said: "Under the Federal Disability Discrimination Act - as well as under equivalent laws in all Australian States - it is unlawful to discriminate against a person on the grounds of their disability by having a Web site which they cannot access." ... "The World Wide Web Consortium has developed Web access guidelines, and non-compliance with them by the operators of Australian Web sites is in breach of the Act."

And, to demonstrate how the Australian law has already been applied, probably the most famous of all actions taken by disabled Web users was when Bruce Maguire (a blind person) cited the Act in a claim against the Sydney Organizing Committee for the Olympic Games, which were held in 1999/2000. He complained to the committee that images on the Olympics.com Web site had no text equivalent for screen readers and Braille displays. SOCOG failed to react to directives and were fined A$20,000.

There are similar clauses in the UK Disability Discrimination Act, the US Americans with Disabilities Act, the Canadian Human Rights Act, plus various provincial, state, and territorial human-rights codes. Following settlement of the claim and the subsequent fine on SOCOG, the Toronto-based Web Content Consultancy contenu.nu stated that: "The case of Maguire vs. SOCOG will inevitably come into play as a precedent for legal cases worldwide."

Accessibility Issues in Web Pages

Web pages today are very different from the original vision of the World Wide Web pioneers. They saw it as a sharing and publishing environment for scientific information, rather than the public network offering online retailing, the source of references material on every topic under the sun, and the general entertainment arena that it has now become.

Wide public access and the continuing commercialization of the Web have brought changes in the type of content it offers. The most remarkable change has been the move away from the functional, mainly text-based, types of pages. Today, the Web is ruled as much by designers and graphic artists as by developers and network specialists.

Web pages have become more complicated. No longer is it permissible for your company "home page" to contain a picture of your offices and a simple text menu for the services you offer. Now you have to have drop-down or pop-up menus with myriad links, graphics (preferably animated), and dozens of headlines that lead to press releases, new product details, or testimonials of your services.

All this is fine if your visitors can look at the page and easily identify the areas that interest them, or scan up and down the links to other pages looking for what they want. And, should they stray to the wrong page, it's usually pretty obvious from a quick glance. However, things are nowhere near this easy for all visitors.

A proportion of visitors will have difficulties with most current Web sites, which are designed almost without exception for people who have reasonable eyesight and are using a pointer device such as a mouse, trackball or graphics tablet. There are many people to whom one or both of these conditions cannot be applied. Often they will be using a specially designed user agent, or maybe a simple text-based browser. Or it might be aural page reader, which reads the contents of pages out loud, or a Braille reader that translates the text into a format that can be read by fingertip on a special output device.

For all these types of device, even the most basic Web site or Web application (designed and tested only in a modern graphical browser such as Internet Explorer) can be hard to read. At best, navigation through the site can be challenging, and certain parts of the content may be meaningless. Graphics and pictures won't be displayed on text-based devices, while color-blind users may not be able to distinguish between the different lines or pie segments in your charts. At worst, it may be completely impossible use online forms, to even access parts of the site at all.

Areas for Improvement

Accessibility problems for Web sites generally fall into three areas:

  • Navigation issues - how easy is it to find and load specific pages without images and without the traditional pointing devices?

  • Visual content issues - do parts of the site depend solely on images and/or colors to provide information, which color-blind, partially sighted and blind users will not be able to access?

  • Data input issues - on pages where the user has to interact with controls, is this possible without a mouse, graphics tablet or other type of pointing device?

Unfortunately, it's not easy to cleanly subdivide the techniques you should be using to improve accessibility into these three simple categories. Some features span more than one category, as you'll see when we look at them in more detail. This article is about the accessibility improvements in ASP.NET 2.0. These improvements are mostly implemented as new or updated features for the sever controls that are a fundamental part of ASP.NET. So, it makes sense to look at them on a control-by-control basis. However, for completeness, we've summarized the general technical recommendations that are part of the W3C Web Accessibility Initiative in the next section. Not all of the topics it covers are applicable to this article, as they are not directly achievable just through the enhancements to the ASP.NET 2.0 control set.

Recommendations for Maximizing Accessibility

This section summarizes the recommendations for improving accessibility for visitors in your Web pages and Web applications. For more details see the W3C site at http://www.w3.org/TR/WCAG20/.

Assisting Navigation

  • Provide one or more hyperlinks that point to the content areas of each page as the first item in that page (before any headings, images, etc). These "skip to" links allow users to go straight to the content without having to navigate past your menu bars or other sets of site navigation links. Alternatively, use CSS absolute positioning to place the menu bars or site navigation links at the end of the page source, yet still allowing them to appear at the top or left of the page when rendered in the browser.
  • Make the structural layout of every page similar (if this is appropriate for the content)
  • Include a meaningful <title> element in every page
  • Avoid self-referring links (links that reference the current page)
  • Avoid meaningless text such as "click here" in hyperlinks
  • Lay out the content of HTML tables so they can be read from left to right (rather than vertically) and still make sense
  • Use <th> elements for the columns and row headings in a table
  • Add a scope attribute to all table row and column headers, to indicate that the header cell identifies the data in that row (scope="row") or column (scope="col"). The new and updated server controls in ASP.NET 2.0 make this much easier
  • Put control captions in the "expected" places on an HTML <form>, for example to the left of a textbox or list control and to the right of a radio button or checkbox
  • Use <label> elements on complex forms, with the for attribute to link them to the control to which they apply.

Identifying Elements and Accessing Alternate Content

  • Include a name and a title attribute on all <frame> elements and include a <noframes> section for user agents that don't support frames, explaining how to access the content in this case
  • Include a title attribute in all interactive elements, such as <a>, <input>, <select>, <textarea>, etc.
  • Include an alt attribute on every image element, containing a brief but meaningful text description of the image
  • Use the longdesc attribute or the widely accepted "d-link" approach to provide a link for any content that is graphical in nature to display a separate page containing a description of the content. Remember that aural content may not be suitable for all users, and so provide a text description or transcript where possible as well.
  • Always include alt="" (an empty string) on images that do not contribute to page content
  • Include an alt attribute in every <area> definition of a client-side image map, and avoid server-side image maps - which cannot be described to user in any kind of meaningful way

General Recommendations

  • Use generic font sizes such as x-small and large to allow the user to enlarge the display font in their browser
  • Provide alternative text content, where possible, inside all <object> or <applet> elements, but outside any <param> elements they might contain
  • Do not generate visual content with client-side script, or - if you must - use a <noscript> section for alternative content

CSS Style Sheets

  • Link your CSS style sheets using a <link> element so that the user agent can decide whether to download them or not, and they can be cached and reused in other pages if applicable: <link rel="stylesheet" title="Default" href="defaultstyle.css" type="text/css" />
  • Consider providing multiple style sheets that specify (for example) monochrome colors or larger text sizes. Use a specific value for the title attribute of each one, and indicate that it is an alternative stylesheet by using rel="alternate stylesheet" in the <link> element.
  • Note that not all browsers and user agents allow the user to specify the stylesheet to be applied, though some allow users to specify their own custom stylesheet
  • Page readers and devices output content in the order it occurs in the page source. CSS absolute positioning can be used to position elements in Web browsers while providing optimum order for page readers, for example, menus and non-informational content can be placed visually at the top of the page, while being located at the end of the page source

    Testing Your Sites and Applications

    Appreciating that you need to improve accessibility in your own sites, or build it into new sites, is only half the battle. Knowing where to start, what to do, and how best to do it is not easy. There are tools to help you check your pages. Examples can be found at the Watchfire 'Bobby' Accessibility Test Tools site (http://bobby.watchfire.com/), UsableNet (http://www.usablenet.com/) and at http://cast.org/products/. Guidelines and standards can be found at W3C's own site: the Web Content Accessibility Guidelines are at http://www.w3.org/TR/WAI-WEBCONTENT/, and the W3C Policy and Laws Guide by Country are at http://www.w3.org/WAI/Policy/.

    Other more direct techniques are to test your site using one of the specialist browsers or user agents. Try accessing your pages in a text-only browser such as Lynx (http://lynx.browser.org/) to see if you can navigate through all the pages without a mouse, and understand what they contain when there are no images. Or try an aural page reader, such as the IBM Home Page Reader (http://www-306.ibm.com/able/solution_offerings/hpr.html). Turn off your screen and see if you can navigate through your own site when the links and content are read out aloud.
    Server Control Accessibility Enhancements in ASP.NET 2.0

    In this article (which is divided into two parts), I'll demonstrate the main changes to the server controls provided with ASP.NET in comparison to those in ASP.NET 1.x, concentrating almost solely on accessibility issues. I've provided four example pages that you can experiment with and that show the various new controls and properties in use.

    • Example 1, in this part of the article, demonstrates the new HtmlLink control, the AssociatedControlID property of the Label control and the use of a hotkey, plus the use of table captions and table row and column headers.
    • Example 2, which is the first of the three examples in part two of this article, demonstrates the use of the AssociatedHeaderCellID property and the use of headers and scope attributes when generating HTML tables dynamically.
    • Example 3 demonstrates the improved support for the alt attribute for images.
    • Example 4 demonstrates the new LabelAttributes and InputAttributes properties of the CheckBox control.

    You can download the examples from http://www.daveandal.net/articles/v2accessibility/.

    Example 1: Accessibility with HtmlLink and GridView Controls

    The first example demonstrates some useful features (as concerns accessibility) of two new controls, as well as additional properties for some existing controls:

    • The new HtmlLink control is used automatically (instead of the HtmlGenericControl) when ASP.NET sees a <link> element that contains the runat="server" attribute in the page. The HtmlLink control exposes the Href property, allowing the href attribute of the resulting <link> element to be set programmatically. However, to set other attributes (for example rel and type when linking a CSS stylesheet), you have to resort to using the Attributes collection.
    • The Caption property, and the associated CaptionAlign property, can be used to display a descriptive caption above, below, or to the left or right of a table. These properties are available on the Calendar, DetailsView, FormView, GridView, Table, DataList and DataGrid controls.
    • The UseAccessibleHeader property, when set to True, causes a control that generates an HTML table to add a scope attribute to each header cell. Non-visual user agents can use this attribute to determine how the header relates to the cells within the table -- in other words if it is a column header (scope="col") or a row header (scope="row"). This makes it easier for the user to understand what the contents of a table represent. This property is available on the Calendar, GridView, DataList and DataGrid controls.
    • The AccessibleHeaderText property is used to specify text that explains what each column header means in more detail, without being visible in the normal output (and therefore not disturbing the layout of the table). This property is available on controls that are used to generate columns or rows in a GridView and DetailsView control, namely BoundField, AutoGeneratedField, ButtonField, CommandField, CheckBoxField, HyperlinkField, ImageField, and TemplateField.
    • The RowHeaderColumn property is set to the name of the column in a GridView control that contains the value that most readily identifies each row (for example the product name column in a list of products). The values in this row are then displayed in <th> elements, rather than the usual <td> elements, and the scope="row" attribute is added to each one. This property is available only on the GridView control.
    • The AssociatedControlID property causes the Label control to generate a <label> element (rather than the usual <span> element). It also adds a for attribute to the <label> element, so that the browser will link it to the specified interactive control (such as a text box, check box or list control). This allows a hotkey to be defined for the Label control that will move the input focus directly to the associated control. This property is available only on the Label control.

    Linking a CSS Stylesheet with the New HtmlLink Control
    Although you can achieve the same effects with an HtmlGenericControl in ASP.NET 1.x, the new HtmlLink control makes it easier to declaratively or dynamically insert <link> elements into your pages and then manipulate the properties of these elements. A prime use for the <link> element, especially when maximizing accessibility, is to specify one or more stylesheets for your page(s). For example:

    <link id="MyLink" runat="server" href="style.css" rel="stylesheet" 
    type="text/css" />

    The HtmlLink element exposes a property named Href that sets the href attribute of the resulting <link> element, and so the URL of the stylesheet can be manipulated at runtime by reading and setting the value of this property. The remaining attributes in the declaration do not correspond to properties of the control. However, the ASP.NET server controls simply copy any unrecognized attributes in the declaration directly onto the element that they generate.

    An alternative approach is to dynamically generate the HtmlLink elements for a page. The declaration of the first part of the example page listed below (example1.aspx) contains no <link> elements:


    <head id="elemHead" runat="server">
    <!-- link elements will be inserted here -->
    <title>DataGrid Control Enhancements</title>

    However, the server-side code contains a Page_Load event handler that creates new instances of the HtmlLink control, sets their properties, and inserts them into the page. It sets the Href property of each one to the URL of the stylesheet, but the remaining attributes can only be set dynamically by accessing the Attributes collection of the control directly.

    The first section of the Page_Load event handler (shown below) is not relevant to this part of the discussion. It simply sets the ConnectionString property of a SqlDataSource control declared later in the page, which is used to populate the GridView control you'll meet in a while.
    <script runat="server">
      Sub Page_Load()
        ds1.ConnectionString = ConfigurationSettings. _
        ' generate new HtmlLink control for "Standard" stylesheet
        Dim oLink1 As New HtmlLink()
        With oLink1
          ' set properties and add required attributes
          .Href = "style.css"
          .Attributes.Add("rel", "stylesheet")
          .Attributes.Add("type", "text/css")
          .Attributes.Add("title", "Default")
        End With
        ' insert into <head> section as a child control
        ' repeat for "Large Text" alternate stylesheet link
        Dim oLink2 As New HtmlLink()
        With oLink2
          .Href = "style-large.css"
          .Attributes.Add("rel", "alternate stylesheet")
          .Attributes.Add("type", "text/css")
          .Attributes.Add("title", "Large Text")
        End With
      End Sub

    Notice that both stylesheet links have a title attribute added. This attribute specifies the text that users will see for each one in the list of stylesheets, if their browser allows them to choose the stylesheet to use. This option is not available in Internet Explorer 6, which uses the last <link> element that contains the attribute rel="stylesheet". The usual approach is to use the title Default for the default stylesheet, and then rel="alternate stylesheet" and a descriptive value for the title attribute in all alternative stylesheet links. Internet Explorer will ignore stylesheet links that contain a rel="alternate stylesheet" attribute in the <link> element.

    The new HtmlLink controls are inserted into the control tree of the page as children of the <head> element. The <head> element is declared as a server control (with the runat="server" attribute - see the listing above), and so it is represented in the control tree by an instance of the new HtmlHead control that is added to ASP.NET in version 2.0. The HtmlHead control (like almost all ASP.NET server controls) has a Controls collection for its child controls. The Add method of this collection adds the specified control, in this case the new HtmlLink controls, to the end of the Controls collection.

    The output that is generated from the code shown above is:

    <link href="style.css" rel="stylesheet" type="text/css" title="Default" />
    <link href="style-large.css" rel="alternate stylesheet" type="text/css" 
          title="Large Text" />

    Figure 1 shows the page displayed in Mozilla 1.5, which allows the style sheet to be selected. You can see the effects of choosing the "Large Text" stylesheet in this screenshot.

    Figure 1 - Choosing a stylesheet for example1.aspx in Mozilla

    Connecting a Label Control and Specifying a HotKey
    The next section of control declarations in the example1.aspx page demonstrates the use of the new AssociatedControlID property of the Label control, along with a couple of other properties that were available in ASP.NET 1.x. When you build pages that contain interactive controls, a useful feature for all users, not just those with special accessibility requirements, is to provide hotkeys that move the input focus directly to a specific control.

    By using a combination of the AssociatedControlID, AccessKey and TabIndex properties, you can make forms or other pages containing interactive controls much easier for all your visitors to use. Setting the TabIndex allows you to control the order that the input focus moves from one control to the next. By default, the browser uses the order of the control declarations within the page. In complex forms, you can use the TabIndex property to make the input focus move down the columns of a table, rather than the default of moving across the rows, or force it to follow whatever path through the controls you require. Of course, the path you choose should be an "obvious" one, so as not to confuse users who will wonder where the input focus went if it jumps from control to control in some non-intuitive way!

    The AccessKey property specifies the key that, when pressed in conjunction with the Alt key, will move the focus to that control on the page (there are some keys you can't use, such as Alt-T, which opens the Tools menu in Internet Explorer). It's also useful to indicate the hotkey to the user by underlining that letter in the caption of the control - it works well with most controls, although it doesn't work with the ASP.NET Button control. The listing below demonstrates the use of the TabIndex and AccessKey properties, along with underlining the corresponding letter in the Label control that acts as the caption of the TextBox:

    <asp:Label id="lblProduct" runat="server" 
               Text="<u>P</u>roduct Name:" 
               TabIndex="0" />  
    <asp:TextBox id="txtProduct" runat="server" 
                 Text="A" />
    <asp:Button runat="server" id="bntGo" Text="Go" 
                ToolTip="Start search for matching products" 
                TabIndex="2" />

    Because a Label control can't receive the focus in the browser, the focus will move to the next control in the declarative or tabbing order of the page. So, in the code above, the TextBox will automatically get the focus when Alt-P is pressed. However, there is no external indication that this will happen, especially for users who depend on aural page readers or other specialist devices. The new AssociatedControlID property solves the problem by connecting the Label control to the interactive control that it refers to, by adding the for attribute to it. For example, the code listed above generates the following HTML when rendered to the client:

    Figure 2 shows the appearance of the controls in Internet Explorer. Bear in mind, however, that not all graphical browsers fully support hotkeys and the use of associated labels.
    Captions, scope, and headers Attributes in the GridView Control
    The final section of code in the example1.aspx page demonstrates the use of some accessibility-oriented properties of the new GridView control in ASP.NET 2.0. The UseAccessibleHeader property is also available on the Calendar, DataList and DataGrid controls, while the other two properties (AccessibleHeaderText and RowHeaderColumn) are specific to the new GridView control.

    The code listing below shows the complete declaration of a GridView control, which is populated by a SqlDataSource control shown at the end of the listing (the ConnectionString property of the SqlDataSource control is set at runtime by code you saw earlier in the Page_Load event handler):

    <asp:GridView id="MyGrid" runat="server"
                  Caption="<u>G</u>ridView Example" 
      <HeaderStyle ForeColor="#F7F7F7" Font-Bold="True" 
                  BackColor="#4A3C8C" />
      <RowStyle ForeColor="#4A3C8C" BackColor="#E7E7FF" />
      <AlternatingRowStyle BackColor="#F7F7F7" />
      <PagerStyle ForeColor="#4A3C8C" 
                  HorizontalAlign="Right" BackColor="#E7E7FF" />
      <FooterStyle ForeColor="#4A3C8C" BackColor="#B5C7DE" />
        <asp:BoundField DataField="ProductID" 
                        AccessibleHeaderText="Product Identifier" 
          <ItemStyle HorizontalAlign="Center"></ItemStyle>
        <asp:BoundField DataField="ProductName" 
                        AccessibleHeaderText="Full Product Name" 
          <ItemStyle HorizontalAlign="Left"></ItemStyle>
        <asp:BoundField DataField="QuantityPerUnit" 
                        AccessibleHeaderText="Quantity per Unit" 
                        HeaderText="Packaging" />
        <asp:BoundField DataField="UnitPrice" 
                        AccessibleHeaderText="Price per Unit" 
                        DataFormatString="$ {0:F2}" 
          <ItemStyle HorizontalAlign="Right"></ItemStyle>
        <asp:BoundField DataField="UnitsInStock" 
                        AccessibleHeaderText="Units in Stock" 
          <ItemStyle HorizontalAlign="Right"></ItemStyle>
    <asp:SqlDataSource id="ds1" runat="server"
         SelectCommand="SELECT ProductID, ProductName, QuantityPerUnit,
                        UnitPrice, UnitsInStock FROM Products"
         FilterExpression="ProductName LIKE '@ProductName%'">
        <asp:ControlParameter Name="ProductName" ControlID="txtProduct" 
                              PropertyName="Text" />

    The section of the example page that this code creates is shown in Figure 3.

    Figure 3 - The output generated by the GridView control in Internet Explorer

    Adding a Table Caption
    Most of the declaration is concerned with the mechanics of specifying the columns and the appearance of the control in a normal graphical Web browser. However, there are several sections that are aimed at improving accessibility for users of non-graphical client devices. The HTML table that the GridView control generates has a caption at the top, so that users can immediately understand what the content as a whole represents. This is aligned above the table, and uses a hotkey in the caption to allow the user to jump directly to the table:

      Caption="<u>G</u>ridView Example" 

    Specifying Meaningful Column Names
    Displaying a table of values is easy, as you can see from the code above. There are plenty of controls in ASP.NET that can take a source rowset and display its contents. However, we often spend more time worrying about the layout and appearance of the table, and little time thinking about things like what the column heading actually mean. After all, they have to be fairly short to avoid upsetting the layout of the table - especially when it contains, for example, just columns of numbers.

    In a non-visual user agent or specialist page reader, it's hard to grasp what a table contains in the same way as a sighted user would (for example, by rapidly scanning over the values to get a feel for what they represent). Therefore, good informative column headings are extremely useful. One way to achieve this is to use an attribute to specify a "long description" of the column contents, which is not visible in an ordinary graphical browser.

    Microsoft chose to take advantage of the abbr (abbreviation) attribute that is supported by all visible elements. You can specify the values to be placed in this attribute for any of the column types that are used in the new GridView and DetailsView controls in ASP.NET 2.0. These column types are: BoundField, AutoGeneratedField, ButtonField, CommandField, CheckBoxField, HyperlinkField, ImageField, and TemplateField.

    You just turn off auto-generation of the columns in the control, and then specify the columns you require - setting the AccessibleHeaderText property of each one. In our example, we turn off automatic column generation by adding the attribute AutoGenerateColumns="False" to the declaration of the GridView control. Then we specify the columns we want. The code below is the declaration of the first two columns, showing how we can set the visual header text and the associated header text to different values, both of which are different to the name of the column in the source rowset (as identified by the DataField attribute):

        <asp:BoundField DataField="ProductID" 
                        AccessibleHeaderText="Product Identifier" 
        <asp:BoundField DataField="ProductName" 
                        AccessibleHeaderText="Full Product Name" 

    Look back at Figure 3 to see the output that is generated in Internet Explorer. If you view the source of the page, you'll see the associated header text as in this (abbreviated) listing:

    <table accesskey="G" tabindex="3" id="MyGrid">
     <caption align="Top"><u>G</u>ridView Example</caption>
      <th abbr="Product Identifier">ID</th>
      <th abbr="Full Product Name">Product</th>
      <th abbr="Quantity per Unit">Packaging</th>
      <th abbr="Price per Unit">Price</th>
      <th abbr="Units in Stock">Stock</th>
     ... data rows here ...

    Specifying the Row Headers in a GridView Control
    In Figure 3, you can also see that the values in the column headed "Product" are displayed in bold text. This column is generated from the values in the ProductName column of the source rowset, and it is the ideal column to provide a "friendly name" identifier for each row.

    You can think of the "friendly name" as the value you'd choose to identify a product for a visitor who wasn't familiar with the product range. Obviously, in our example, this is going to be the product name. However, if the output is aimed at users who will already be familiar with all the product identifier numbers (maybe a stock control clerk who is just looking up the price or the quantity in stock) then the product ID column might be a more appropriate choice.

    The values in the column that contains the "friendly name" of each product are effectively the headers for each row in the GridView control output. In other words, a visitor uses the row header value in conjunction with the value in the column header to uniquely identify any other value in that row. In plain English, the value in the price column of the first row in Figure 3 is "the Price of Tofu".

    While this type of instant association in a table is easy for visually capable users, it's a lot more difficult for non-visual user agents or specialist page readers to achieve. To make it easier, two more properties of the GridView control have been set in this example. We set the RowHeaderColumn property to the name in the source rowset that provides the "friendly name" values, and set the UseAccessibleHeader property to True:


    This forces several changes in the output generated by the GridView control:

    • It forces the values in every row in the column defined by the RowHeaderColumn property to be placed in <th> elements (instead of <td> elements). All the column headers are also placed in <th> elements.
    • It adds the scope attribute to each <th> header cell. The column headers gain the attribute scope="col" to indicate that the value in this cell identifies or describes the values in this column. The row headers gain the attribute scope="row" to indicate that the value in this cell identifies or describes the values in the remaining cells of this row.

    The abbreviated listing below shows some of the output generated by the GridView control in our example so that you can see the results more clearly:

    <table accesskey="G" tabindex="3" id="MyGrid">
     <caption align="Top"><u>G</u>ridView 
      <th scope="col" abbr="Product Identifier">ID</th>
      <th scope="col" abbr="Full Product Name">Product</th>
      <th scope="col" abbr="Quantity per Unit">Packaging</th>
      <th scope="col" abbr="Price per Unit">Price</th>
      <th scope="col" abbr="Units in Stock">Stock</th>
      <th scope="row">Tofu</th>
      <td>40 - 100 g pkgs.</td>
      <td>$ 23.25</td>

    Specifying the headers Attribute for Cells in a GridView Control
    While the accessibility improvements that result from setting the RowHeaderColumn and UseAccessibleHeader properties are admirable and a huge advance on the abilities of the grid controls in ASP.NET 1.x, it's relatively easy to improve on this even more by adding in the headers attribute to each non-header cell.

    • Each cell in every row that is not a <th> header cell should have a headers attribute that identifies the row and column headers for this cell. For example, referring back to Figure 3, the cell containing the price of Tofu should carry the attribute: headers="id-of-Price-column-header, id-of-Tofu-row-header". If the column and row headers have ID values "ColumnHeader_Price" and "RowHeader_14" then the headers attribute should be: headers="ColumnHeader_Price,RowHeader_14".

    To achieve this, we have to "interfere with" the generation of the output of the GridView control, by handling one of the events that is raised when each row is being created. The ideal event is RowDataBound, and to handle this we simply add the OnRowDataBound attribute to the declaration of the GridView control, specifying the name of the event handler we want to execute as each row is bound to the data source and the cells for the resulting HTML table row are being generated:

    <asp:GridView id="MyGrid" runat="server"

    The AddHeadersAttr event handler is shown in the next two listings. While it might look complicated, all it does is examine each row - as it is being data bound - and add the appropriate attributes to each cell in that row. If the current row is the header row for the grid (denoted by having the value Header from the DataControlRowType enumeration for its RowType property), the code adds an id attribute to each <th> cell that is created in this row. The value used is the text string "ColumnHeading_" concatenated with the column heading text:

    Sub AddHeadersAttr(ByVal sender As Object, ByVal e As GridViewRowEventArgs)
      If e.Row.RowType = DataControlRowType.Header Then
        ' this is the column header row, so add ID to each column using column name
        ' NOTE: cannot set ID property because this includes the ID of all parent 
        ' controls as well, for example "MyGrid_ctl1_3" instead of just "3"
        For i As Integer = 0 To e.Row.Cells.Count - 1
          e.Row.Cells(i).Attributes.Add("id", _
                         "ColumnHeader_" & MyGrid.Columns(i).HeaderText)
    Notice that we add the id attribute using the Attributes collection to get the value we want. If we set the ID property of the cell directly, the control automatically prefixes it with the IDs of the parent controls - so that the value is guaranteed to be unique within the control tree even if you were to insert two instances of this GridView control into the page using the same ID values for the column header cells. However, as this ID contains the ID of the row as well as the ID of the GridView control, it's not suitable for use here. We need a value that is not dependent on the ID of the column-heading row when we come to add the headers attribute to the data cells in each row.

    If the current row is not a header row, we then check if it's a data row (a row that is bound to the source data rowset). This type of row is denoted by the value DataRow for the RowType property. If it is a data row, it will have one value that is displayed in a <th> cell (the row header) and the remaining values displayed in <td> cells. So, as we iterate through the cells in the row, we have to check the control type.

    If it's the row header (of type DataControlFieldHeaderCell), we add an id attribute to it as we did previously for the column headers, but this time using the text "RowHeader_" and the product ID value from this row. We can obtain the product ID from the DataKeys collection of the GridView control because we specified the attribute DataKeyNames="ProductID" when we declared the GridView control.

    If the current cell in this row is an ordinary <td> data cell (of type DataControlFieldCell), we must instead add the appropriate headers attribute to it. This contains the ID values of the matching column and row headers, which can be built up using the current column name and the product ID (from the DataKeys collection) for the current row.

      ElseIf e.Row.RowType = DataControlRowType.DataRow Then
        ' this is a data row
        For i As Integer = 0 To e.Row.Cells.Count - 1
          Dim oCell As Object = e.Row.Cells(i)
          If TypeOf oCell Is DataControlFieldHeaderCell Then
            ' this is the row header, so add an ID to it using value of ProductID
            CType(oCell, DataControlFieldHeaderCell).Attributes.Add("id", _
                  "RowHeader_" & MyGrid.DataKeys(e.Row.RowIndex).Value.ToString())
            ' this is a data cell, so add the appropriate headers attribute
            CType(oCell, DataControlFieldCell).Attributes.Add("headers", _
                  "ColumnHeader_" & MyGrid.Columns(i).HeaderText _
                  & ",RowHeader_" & MyGrid.DataKeys(e.Row.RowIndex).Value.ToString())
          End If
      End If
    End Sub

    Notice that each value in the DataKeys collection (there is one value for each row) is itself a collection of DataKey instances, because in ASP.NET 2.0 the DataKeyNames attribute can be used to specify more than one column from the source data rowset as the keys for each row. You can access the values in the DataKeys collection using the new methods GetKeyByIndex, GetKeyByName, and the new property AllKeys. Alternatively, the Value property returns the value of the key for a specified row, given a row index, when only a single key is specified in the DataKeyNames attribute of the GridView. The result of adding the event handler shown above is that the HTML table generated by the GridView control now has full accessibility support in the form of scope, id and headers attributes for every cell. The abbreviated listing here shows these attributes:

    <table accesskey="G" tabindex="3"id="MyGrid">
     <caption align="Top"><u>G</u>ridView 
      <th id="ColumnHeader_ID" scope="col" abbr="Product 
      <th id="ColumnHeader_Product" scope="col" abbr="Full Product 
      <th id="ColumnHeader_Packaging" scope="col" abbr="Quantity per 
      <th id="ColumnHeader_Price" scope="col" abbr="Price per 
      <th id="ColumnHeader_Stock" scope="col" abbr="Units in 
      <td headers="ColumnHeader_ID,RowHeader_3">14</td>
      <th id="RowHeader_3" scope="row">Tofu</th>
      <td headers="ColumnHeader_Packaging,RowHeader_3">40 - 100 g 
      <td headers="ColumnHeader_Price,RowHeader_3">$ 23.25</td>
      <td headers="ColumnHeader_Stock,RowHeader_3">35</td>

    Viewing the Results in the IBM Home Page Reader
    To get a feel for the way that these attributes can be used, and the huge advantage they provide, the IBM Home Page Reader allows the user to switch into Table Navigation Mode when there is an HTML table on the current page. It first describes the table by reading the caption, and then announces the number of rows and columns in the table.

    Next, it reads the text in the header row, and continues for each cell in the table. However, the user can press the arrow keys at any time to move the current "reading point" to any other cell - moving up, left, right or down. As it moves from one column to the next, the reader uses the extra accessibility attributes to announce the header of the column that the user has moved to. When the user moves from one row to the next, it announces the value of the row header.

    So, for example, when moving from the Price cell in the row containing the values for Aniseed Syrup to the row containing values for Alice Mutton, the reader will announce "Alice Mutton $39.00" (see Figure 4).

    Figure 4 - How the IBM Home Page Reader uses the scope and headers attributes

    The lower section of the window contains the text that is read to the user by the built-in speech synthesizer. The highlighted word is that currently being read aloud to the user. The IBM Home Page Reader is a combined graphical, text and aural Web browser with a raft of great features. See the section "Testing Your Sites and Applications" (earlier in this article) for details of how to obtain a copy of the IBM Home Page Reader, the Lynx text browser, and other specialist user agents.

    This is the end of the first part of an article on using the new accessibility features in version 2.0 of ASP.NET. The second part continues the theme by looking at creating headers and scope attributes in custom HTML tables.

    About the Author

    Alex Homer began his love-hate relationship with computers in 1980 with the Altair and Sinclair Z80. Since 1994 he has been working with and writing about various programming technologies - from databases to building Help systems. However with the growth of the Web, and particularly as ASP rapidly become a viable application platform, he concentrated almost entirely on this topic. Alex has written or contributed to more than 30 books on Web development topics for the major publishers, as well as producing articles for ASPToday, DevX and other sites. He is an MVP and a member of the INETA speaker's bureau, and speaks regularly on a range of Web development topics at conferences such as Microsoft PDC, Tech-Ed, ASP Connections, WebDevCon and VS.NET Live. In what spare time is left, he runs his own software and consultancy company Stonebroom Limited.

0 0



取 消