摘要: Microsoft ASP.NET 2.0 具有很多有用的功能,能帮助您设计和生成符合 XHTML 和可访问性标准的 Web 站点。本文探讨如何以及为什么生成这些符合标准的站点。
简介
Web 标准使您能通过最少的工作,生成可被最广大受众访问的 Web 站点。Web 标准的承诺是:只需设计页面一次,即可让该页以完全相同的方式在任何现代的浏览器中显示和工作。例如,在按照标准生成以后,旨在在 Microsoft Internet Explorer 中以某种方式显示的页可在其他浏览器(如,Mozilla Firefox、Netscape Navigator、Opera、Camino 和 Safari)中以相同的方式显示,而无需完成任何额外的工作。
Web 标准的一个额外好处是 ― 使 Web 站点更易于为残疾人士访问。这是一个范围广泛的受众群体,包括视力衰退的中年人士,刚刚在滑雪时跌断胳膊的人士,以及完全失明的人士等。使用标准可避免无意中阻止那些具有暂时性或永久性身体残疾的人士访问 Web 页。
对于生成满足公共 Web 标准的 Web 站点而言,Microsoft ASP.NET 2.0 框架是最佳的框架。特别强调的是,ASP.NET 2.0 框架中的每个控件都按照 XHTML 和可访问性标准进行了全面的检查和测试。此外,Microsoft Visual Studio .NET 2005 还包含一些新工具,用于按照 XHTML 和可访问性标准验证 Web 页。
本文的目的是为您提供有关 XHTML 和可访问性标准的概述,并说明如何利用 ASP.NET 2.0 和 Visual Studio .NET 2005 来满足这些标准。在本文的结尾,将分步演练以下功能,即创建能够同时满足 XHTML 和可访问性标准的 ASP.NET 2.0 Web 站点。
生成 XHTML Web 站点
HTML 在正式的场合已经过时了。World Wide Web Consortium (W3C) 于 2000 年 6 月 26 日发布了 XHTML 的第一个版本作为推荐标准。XHTML 标准的目标是取代 HTML。按照 W3C 的说法,“XHTML 是 HTML 的继承者”(http://www.w3.org/MarkUp/)。
XHTML 标准的制定者具有两大目标:
•
在文档结构和表示形式之间创建更明显的分离。
•
将 HTML 重新表示为 XML 的应用程序。
为了实现第一个目标,W3C 一直在坚定地从 HTML 中删除纯粹描述性的元素和属性(他们是从 HTML 4.0 开始这一过程的)。例如,XHTML 1.0 Strict 不包含诸如 标记之类的元素或诸如 bgcolor 属性之类的属性,因为这些元素和属性完全用于描述文档的外观,它们与文档的结构没有任何关系。
W3C 一直在努力使 Web 站点设计人员和开发人员摒弃特定标记应当具有特定外观这一观念。例如,您可能会认为
标记(标题标记)的用途是在页中呈现大的加粗文本。这实际上是错的。
标记用来在文档中标记标题而不是其他任何东西。如何呈现标题标记由浏览器确定。视力衰退的人士使用的屏幕阅读器可能利用抑扬顿挫的声音来大声朗读标题标记的内容。不支持多个字体大小的 PDA 可能用闪烁文本呈现标题标记的内容。
您不应当试图使用诸如
标记之类的页元素来控制 Web 页的外观。相反,您应当通过使用层叠样式表来指示 Web 页的外观。而且,您所使用的层叠样式表应当是外部 层叠样式表。请使用标记和属性来标记文档的结构,而使用样式表来控制文档的表示形式。
XHTML 的第二个目标是迫使 HTML 开发人员遵守更为严格的 XML 规则。按照 W3C 的说法,“XHTML 1.0 是 HTML 4.01 的作为 XML 1.0 应用程序的修订”(http://www.w3.org/MarkUp/)。换句话说,使用 XHTML 生成 Web 页时,实际上是在创建 XML 文档。
XML 文档具有比 HTML 文档更严格的语法。例如,XML 区分大小写,所有 XML 属性都必须放在引号内,而且 XML 标记不能重叠。强迫 Web 站点开发人员和设计人员遵守有更高要求的语言规则有很多好处。
好处之一,用 XHTML 标记编写的页具有更高的跨浏览器、跨设备和跨操作系统兼容性。如果在浏览器中打开传统的 HTML 页,浏览器将千方百计地呈现该页。浏览器将试图呈现该页,即使您的 HTML 一团糟。例如,Internet Explorer(以及 Firefox 和 Opera)能够很好地显示下面的 HTML 页。this is bold and italic and this is bold
Internet Explorer 会恰当地显示该页 ― 即使该页缺少 和 开始标记, 标记不具有匹配的结束标记,并且开始和结束 标记的大小写不一致。所有主要的浏览器都能适应几乎任何 HTML 标记“混合物”,并且不顾一切地呈现一些内容。
浏览器的这种适应行为是危险的,因为不同的浏览器(或相同浏览器的将来版本,或在不同操作系统上运行的相同浏览器)可能以不同方式呈现错乱的 HTML。实际上,对于最新版本的 Internet Explorer、Mozilla Firefox 和 Opera 而言,它们呈现无效 HTML 的方式惊人地一致。但是,一旦开始违反游戏规则,就不会得到任何保证。
然而,如果用 XHTML 的更严格的规则编写 Web 页,那么 Web 页就更有可能以一致的方式与当前浏览器协作,并且它们将继续与当前浏览器的未来新版本协作。对于任何公司而言,几乎都不具备针对每个浏览器、在每个操作系统和每个设备上测试其 Web 站点的资源。如果按照 Web 标准编写页面,那么就不必具有这样的资源。
XHTML 标准的版本
有三个版本的 XHTML 1.0,它们分别对应三个版本的 HTML 4.01:
•
XHTML 1.0 Transitional
•
XHTML 1.0 Strict
•
XHTML 1.0 Frameset
XHTML 1.0 Transitional 包含 HTML 4.01 Transitional 中的全部标记和属性。引入 XHTML 1.0 Transitional 标准的目的是,使现有 HTML 设计人员和开发人员无需经历太多的痛苦就能迁移到 XHTML。
XHTML 1.0 Strict 与 XHTML 1.0 Transitional 的不同之处在于,它在文档结构和表示形式之间实施了一种更为明显的分离。与 XHTML 1.0 Transitional 不同,XHTML 1.0 Strict 强迫您使用层叠样式表来控制页的外观。
XHTML 1.0 Frameset 文档意在成为使用 标记将浏览器划分为多个框架的文档(XHTML 1.0 Transitional 和 Strict 页不能包含 标记)。
W3C 还发布了 XHTML 1.1 以作为推荐标准(2001 年 5 月 31 日)。XHTML 1.1 非常类似于 XHTML 1.0 Strict。二者的主要区别在于,可以用附加模块扩展 XHTML 1.1 以便支持新元素。例如,可以生成特定的 XHTML 1.1 页,该页还包含 MathML(数学标记语言)、SVG(可伸缩向量语言)或创建的自定义模块中的元素。
最后,W3C 正在制订 XHTML 2.0 推荐标准。因为 XHTML 2.0 仍然处于起草阶段,并且当前没有 Web 浏览器支持该标准,所以我们不在本文讨论它。
ASP.NET 2.0 框架和 Visual Studio .NET 2005 面向 XHTML 1.0 Transitional。该标准是 XHTML 标准中限制性最低的,而且它是与现有 HTML 页最兼容的标准。但是,还可以生成面向 XHTML 1.0 Strict 标准甚至 XHTML 1.1 标准的 ASP.NET 2.0 页(请参阅后面的“配置 XHTML 一致性”一节)。
(请注意,默认情况下,ASP.NET 框架的 Beta 2 版本面向 XHTML 1.1。 ASP.NET 2.0 框架的最终版本将面向 XHTML 1.0 Transitional。)
创建 XHTML 页
与 HTML 页不同,XHTML 页必须是标准格式且有效的 XML 文档。XHTML 1.0 推荐标准的第 4 部分对 HTML 和 XHTML 之间的区别进行了总结。这里给出生成有效 XHTML 页的最重要需求的列表:
页必须包含有效的 XHTML DOCTYPE。
有效的 XHTML 页必须在其任何内容之前包含一个 XHTML DOCTYPE。当在 Visual Studio .NET 2005 或 Microsoft Visual Web Developer 中创建新的 ASP.NET 页时,该页中将自动包含 XHTML 1.0 Transitional 的正确的 DOCTYPE。下面列出四个标准的 XHTML DOCTYPE:
XHTML 1.0 Transitional
XHTML 1.0 Strict
XHTML 1.0 Frameset
XHTML 1.1
向页中添加 DOCTYPE 会影响该页在浏览器中的呈现方式。请参阅以下标题为“XHTML 和 DOCTYPE 切换”的一节。
根元素必须引用 XHTML 命名空间。
XHTML 页的开始 标记必须指定默认命名空间 http://www.w3.org/1999/xhtml。以下是 XHTML 1.0 Transitional 页的有效开始 标记的示例:
所有元素和属性名都必须小写。
XML 区分大小写。因此,在
标记和
标记之间存在差异。只有前者是有效的 XHTML 段落标记。
属性值必须始终放在引号内。
确保始终将属性值放在双引号或单引号中。例如,以下是无效的 XHTML。Next
在该示例中,href 属性缺少引号。以下代码是有效的 XHTML。Next
您可以通过选择菜单选项 Tools、Options、Format,将 Visual Studio .NET 2005 和 Visual Web Developer 配置为自动将属性值放在引号内。
所有具有开始标记的非空元素都必须具有匹配的结束标记。
如果具有开始
标记,则必须包含结束 标记来标记段落的结束。对于根本不包含任何内容的标记,例如
标记,可同时提供开始和结束标记
,也可以使用空元素简写
。
为使 XHTML 页与现有的 HTML 浏览器向后兼容,需要小心处理打开和关闭标记的方式。例如,现有 HTML 浏览器倾向于将开始和结束
标记错误地解释为两个
元素。因此,您应当使用空元素简写
。
此外,除非您小心地在结束斜杠之前添加一个空格,否则现有 HTML 浏览器在处理空元素简写时会出现问题。因此,应当使用
[space] />(而不是)向页中添加
元素。
不得存在重叠标记。
可以使标记嵌套,但是不允许使标记重叠。例如,以下 XHTML 是有效的。This is bold and italic
但是,以下 XHTML 是无效的。This is bold and italic
不得存在属性最简化。
所有属性都必须具有值,即使该值看起来有一点儿奇怪。例如,标记是无效的 XHTML,因为 checked 属性不具有值。该标记应当写成。
必须使用 id 属性而不是 name 属性。
在 HTML 中,可以使用 name 属性来标识 、、、、、和元素。尽管可以使用 name 属性生成 XHTML 1.0 Transitional 页,但在 XHTML 1.0 Strict 和 XHTML 1.1 标准中已经将 name 属性删除。您应当改而使用 id 属性来标识这些元素。必须将使用 CDATA 节并非对所有浏览器都有效。例如,Internet Explorer 会将JavaScript 使用 /* 和 */ 来标志注释的开始和结束。因此,CDATA 节对 JavaScript 隐藏,但不对分析该页的浏览器隐藏。总之,较好的做法是将样式规则和脚本放在外部文件中,而从 XHTML 页中引用这些文件。通过使用外部样式表和脚本,能够避免上述所有问题。XHTML 和 ASP.NET 控件默认情况下,ASP.NET 2.0 框架中包含的每个 ASP.NET 控件都呈现有效的 XHTML。换句话说,向页中添加 ASP.NET 控件时,您无需完成任何特殊工作来生成有效的 XHTML 标记。例如,如果您向页中添加 GridView 控件,则 GridView 控件会生成有效的 XHTML 标记。这里需要澄清三个要点。首先,包含 ASP.NET 控件的页的源代码不会通过 XHTML 验证。验证 ASP.NET 页时,需要验证页呈现的内容(在 Internet Explorer 中选择 View Source 时看到的所有内容),而不是该页的源代码。其次,在创建 ASP.NET 页时,没有任何事情阻止您编写无效的 XHTML。您当然可以向 ASP.NET 页中添加希望添加的任何标记。例如,如果向页中添加 标记,那么页将不会通过 XHTML 1.0 Strict 验证。最后,当您使用自定义 ASP.NET 控件时,没有任何保证。如果购买第三方 ASP.NET 控件(例如,一个一流的增强 DataGrid 控件),则该控件可能会呈现有效的 XHTML,但也可能不会。保证不犯错误是控件供应商的责任。验证 XHTML 页Visual Studio .NET 2005 和 Visual Web Developer 会在生成 Web 页的过程中自动验证该页的有效性。通过在违反规则的内容下添加绿色或红色波浪线,在“Source”视图中指出验证问题。红色波浪线对应于诸如缺少结束标记之类的验证错误。绿色波浪线对应于验证警告,例如,使用了已否决的标记。将鼠标悬停在任何波浪线上方,可查看包含验证错误或警告消息的工具提示(参见图 1)。或者,还可以在 Error List 窗口中查看验证错误或警告的列表(依次选择 View、Other Windows、Error List)。




runat="server"Last Name:
在为 ASP.NET 控件提供标签时,应当使用 ASP.NET Label 控件,而不是 HTML标记。在将一个 ID 分配给 ASP.NET 控件(如 TextBox 控件)时,呈现到浏览器中的 ID 可能与您分配给该控件的 ID 不同。因此,如果使用标记,则标记中的 ID 可能与所呈现的 TextBox 控件的 ID 不匹配。另一方面,如果使用 ASP.NET Label 控件,则不必担心该问题。ASP.NET CheckBox、RadioButton、CheckBoxList 和 RadioButtonList 控件自动呈现标记。在使用这些控件时,请小心使用 Text 属性来标记控件的文本。您不应该执行以下操作。Include Gift Wrap相反,请执行以下操作。对于通过屏幕阅读器与 Web 页进行交互的用户,大型表单也可能产生问题。在聆听大型表单的内容时,很容易忘记正在聆听该表单的哪个部分。在显示大型表单时,将该表单划分为多个小块是一个好主意。您可以通过使用标记将单个表单划分为多个部分。以下示例说明如何使用该标记。Contact Information... form fieldsPayment Information... form fields该表单通过标记划分为两个子表单。标记用来标记这些子表单的用途。在 Internet Explorer、Firefox 和 Opera 中显示时,这些子表单被边框直观地划分为多个单独的区域(参见图 5)。但是,重要的是要记住,标记的主要用途是实现可访问性。如果不喜欢标记的可视化外观,那么可通过样式表规则修改该标记的外观,或通过使用 CSS display 或 visibility 属性将该标记完全隐藏。

Runat="server" /tabindex 属性用来控制表单域的 Tab 键顺序。因为第一个表单域具有的 tabindex 值为 1,所以当用户第一次按 Tab 键时,该页中任何出现在该表单之前的其他元素都被跳过。在使用 Internet Explorer 或 Firefox 时,按 ALT+F 可自动将焦点移至 First Name 文本框。如果按 ALT+L,则焦点会自动移至 Last Name 文本框。在使用 Opera 时,必须首先按 SHIFT+ESC,然后才能选择访问键。请注意,First Name 和 Last Name 标签的第一个字母都带有下划线。通过为字母添加下划线,可以为 Web 站点的用户提供访问键的直观表示。这是在 Microsoft Windows 应用程序中标记访问键的标准方式。但是,还有其他在表单中指示访问键的推荐方法(请参阅 http://www.cs.tut.fi/~jkorpela/forms/accesskey.html)。使用下划线指示访问键的一个问题是无法为按钮中的字符添加下划线,并且超链接已经带有下划线。例如,下面的 Button 控件并不像您预期和希望的那样工作。Text="Submit" Runat="server" /在呈现该 ASP.NET Button 控件时,会显示实际文本 Submit,而不是显示带下划线的S字符。ASP.NET Button 控件呈现 HTML标记,但遗憾的是,标记不支持下划线。您可能认为可以通过使用样式规则解决该问题。遗憾的是,当前不存在以下这种跨浏览器兼容方法:即,使用层叠样式表为标记中的单个字符加下划线。如果您愿意在页中使用客户端 JavaScript,则可以解决该问题。清单 4 中的页包含的 JavaScript 根据是否按下 ALT 键而显示或隐藏所有访问键。当按下 ALT 键时,会弹出一个框,显示访问键键盘组合键(参见图 6)。该脚本在 Internet Explorer 和 Firefox 中都能够正常工作(Opera 不使用 ALT 键来选择访问键)。

access key is fID="lblLastName"AssociatedControlID="txtLastName"AccessKey="l"runat="server"Last Name:
access key is laccess key is s清单 4 中的页包含样式表和客户端 JavaScript。样式表隐藏了由 accessKey 类标识的任何标记的内容。JavaScript 会在 ALT 键已经被按下时进行检测,并且显示标记的内容。请注意,即使 Web 浏览器中禁用了样式表和 JavaScript,该页也能够正常工作。在这种情况下,将总是显示访问键帮助(参见图 7)。


当查看 CNN Web 站点的主页时,您绝对不会看到该链接。该链接中包含的图像是一幅透明的单像素图像。但是,如果您用屏幕阅读器访问该页,则会阅读与该图像相关联的替换文本。盲人可以选择跳过所有导航链接,并直接移至 Web 页的主要内容区域(这与在自动语音系统中按 0 并直接导航到话务员等效)。
“跳过导航”链接已经被集成到多个标准 ASP.NET 2.0 控件中。特别需要指出的是,Menu、TreeView、SiteMapPath、Wizard 和 CreateUserWizard 控件全都支持“跳过导航”链接。
例如,清单 5 中的页包含 ASP.NET Menu 控件。该控件用来显示指向该 Web 站点中其他页的链接列表。
清单 5. SiteMenu.aspx
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
Skip Navigation
id="Menu1" Runat="server"
Here is the main content of the page...
如果查看清单 5 中页的源代码,您将看到以下链接出现在菜单顶部。src="/WebResource.axd?d=ChXz41GuDxNm-7TcWyCl_w2&t=632495684475122400"width="0" height="0" style="border-width:0px;" /该链接包含一幅在您查看该页时不会出现的宽和高皆为零的图像。但是,通过屏幕阅读器访问该页的用户可以选择“跳过导航”链接跳到该菜单的结尾。默认情况下,“跳过导航”链接包含文本 Skip Navigation Links。可以通过更改 Menu 控件的 SkipLinkText 属性修改该值。创建可访问的数据ASP.NET 2.0 框架包含一组丰富的、用于显示数据库数据的控件。这些控件包括 GridView、DetailsView、DataList、FormView 和 Repeater 控件。默认情况下,GridView、DetailsView 和 DataList 控件在 HTML 表中显示数据库记录。在 HTML 表中呈现信息时,如果操作错误,则可能引起可访问性问题。在聆听 HTML 表的内容时,您很容易忘记自己当前在该表中的位置。例如,假设您使用 HTML 表显示一个产品信息列表。在聆听由屏幕阅读器朗读的表内容时,您很容易将某个表单元格所代表的信息搞混,不知道它们是有关产品名称的,还是有关所订购产品数量的,抑或是有关存储这些产品的仓库的代码。在查看 HTML 表时,可通过扫视列或行标题来确定特定单元格的含义。为使表对于使用屏幕阅读器的用户是可访问的,需要显式标记表标题并将这些标题与各个单元格显式关联起来。在创建表以显示数据时,应当始终使用正确的标记来标记列和行标题。表标题应当总是用标记进行标记,如下所示。Product NamePriceMilk$2.33Cereal$5.61在该示例中,标记用来标记以下两个列标题:Product Name 和 Price。一些设计人员避免使用标记,因为他们不喜欢它的默认可视化外观。在大多数浏览器中,标记的内容居中并且加粗。但是,需要记住的是,标记绝不应当用来控制表示形式。如果您希望列标题看起来像标准的表单元格,则您应当添加如下所示的样式规则。th {text-align:left;font-weight:normal}为了使表可访问,还应当显式指明与各个单元格相关联的一个或多个标题。您可以将多个属性用于此目的:scope、headers 和 axis。scope 属性可用来指示表标题是列标题还是行标题。例如,下面的表同时包含列标题和行标题,它们都通过使用 scope 属性的标记进行标记。First TrainLast TrainAlewife5:24am12:15amBraintree5:15am12:18am该表包含 Boston 地铁 Red Line 的时间表(参见图 8)。请注意,每个列标题都包含 scope="col" 属性,而每个行标题都包含 scope="row" 属性。




ConnectionString="Server=localhost;Trusted_Connection=true;Database=Northwind"SelectCommand="SELECT * FROM Categories" Runat="server" /在清单 8 中,外层的 Repeater 控件用来列出产品类别,而内层的 Repeater 控件用来列出匹配产品。下列两个 Helper 函数用来生成 Category Name 和 Product ID 标题的 id 值:GetProductHeader 和 GetCategoryHeader 函数。另外一个单独的名为 GetHeaders 的 Helper 函数用来生成用于 headers 属性的值。清单 8 中的 ASP.NET 页生成如下所示的 HTML 表。IDNamePriceBeverages1Chai 2$18.552Chang$19.00.... remainder of the table请注意,每个标记都包含适当的 headers 属性。创建可访问的 XHTML很多可访问性准则共有的一个主题是这样一个概念 ― 即,Web 页应当符合标准,这样才能成为可访问的页。按照准则,您应当努力使用最新的 W3C 标准(例如,最新版本的 XHTML 和层叠样式表)来生成 Web 站点。特别需要指出的是,在设计 Web 页时,您应当将文档的结构与它的表示形式分开。请使用标记来表示 Web 页的结构,并且使用层叠样式表来控制 Web 页的外观。例如,绝不要仅仅使用元素来缩进文本块。元素的用途是创建原文的引文。如果您希望缩进文本,则应当改而使用层叠样式表 margin 属性。您还应当只在表示数据表时使用标记。尽管使用标记来对 Web 页面进行布局在当前是一种常见的做法,但是,请尽可能改而使用标记。例如,清单 9 中的页具有三列式布局,但是不包含一个标记(参见图 12)。


•
WAVE
示例应用程序:可访问的 XHTML ASP.NET Web 站点
在最后一节中,我们将从头到尾完整地生成一个 ASP.NET 2.0 Web 站点。本白皮书随附有该示例 Web 站点的源代码。您可以下载该示例 Web 站点的源代码,并且在 Visual Web Developer 或 Visual Studio .NET 2005 中打开该 Web 站点。
我们的目标是创建一个完全符合标准的 Web 站点。该 Web 站点将通过 XHTML 1.0 Strict(甚至 XHTML 1.1)验证。而且,该 Web 站点还可供残疾人士访问。它将同时满足 508 节和 WCAG(优先级 1 和优先级 2)可访问性要求。
我们将生成一个名为 Super Super Bookstore Web 站点的网上书店。我们将通过 Amazon 电子商务 Web 服务检索书店的所有书籍清单。Amazon 电子商务 Web 服务为我们提供了足够的免费示例数据,以供我们进行演练(有关 Amazon Web 服务的详细信息,请参阅 http://www.amazon.com/gp/aws/landing.html)。
为简单起见,我们的 Web 站点仅由两个 ASP.NET 页组成:
•
Default.aspx ― 该页显示指定类别中的书籍的列表。
•
Search.aspx ― 该页使您能够搜索满足特定搜索条件的所有书籍。
在幕后,该 Web 站点使用了 ASP.NET 2.0 框架的多项新功能。例如,该 Web 站点使用了一个母版页来创建公共页布局,并且使用了一个主题来创建公共页样式。最后,示例站点使用新的 GridView 和 ObjectDataSource 控件进行数据访问。
访问 Amazon Web 服务
Super Super Bookstore 使用一个名为 Amazon 的公共类来针对 Amazon 书目检索书籍信息并执行搜索。该类包含在清单 10 中。
清单 10. Amazon.vbImports Microsoft.VisualBasic Public Class Amazon
Const SubscriptionId As String = "1CD1NYF3YQ830DG7AM02" '''
''' Attempts to get books in category from cache.
''' If not in cache, call Amazon Web service '''
Public Function GetBooks(ByVal CategoryId As String) _
As AmazonServices.Item()
Dim context As HttpContext = HttpContext.Current
Dim Books As AmazonServices.Item()
If IsNothing(context.Cache(CategoryId)) Then
Books = GetBooksFromAmazon(CategoryId)
context.Cache(CategoryId) = Books Else
Books = CType(context.Cache(CategoryId), _
AmazonServices.Item()) End If Return Books
End Function '''
''' Retrieves books in certain category from Web service '''
Public Function GetBooksFromAmazon(ByVal CategoryId As String) _
As AmazonServices.Item()
Dim service As New AmazonServices.AWSECommerceService()
' Initialize Request
Dim searchRequest As New AmazonServices.ItemSearchRequest
With searchRequest .SearchIndex = "Books"
.Sort = "salesrank"
.ResponseGroup = New String() {"Medium"}
.BrowseNode = CategoryId End With
Dim search As New AmazonServices.ItemSearch With search
.SubscriptionId = SubscriptionId
.Request = New AmazonServices.ItemSearchRequest() _
{searchRequest} End With ' Get Response
Dim response As AmazonServices.ItemSearchResponse = Nothing Try
service.Timeout = 5000
response = service.ItemSearch(search) Catch End Try
If IsNothing(response) Then Return Nothing End If
Return response.Items(0).Item End Function '''
''' Searches for books by calling Amazon Web service '''
Public Function SearchBooksFromAmazon(ByVal Author As String, _
ByVal Title As String, ByVal Keywords As String, _
ByVal PowerSearch As String) As AmazonServices.Item()
' Don't search if nothing to search for
If IsNothing(PowerSearch) And IsNothing(Author) And _
IsNothing(Title) And IsNothing(Keywords) Then
Return Nothing End If ' Initialize Request
Dim service As New AmazonServices.AWSECommerceService()
Dim searchRequest As New AmazonServices.ItemSearchRequest
With searchRequest .SearchIndex = "Books"
.ResponseGroup = New String() {"Medium"}
If Not IsNothing(PowerSearch) Then
.Power = PowerSearch Else
If Not IsNothing(Author) Then
.Author = Author End If
If Not IsNothing(Title) Then .Title = Title
End If If Not IsNothing(Keywords) Then
.Keywords = Keywords End If
End If End With
Dim search As New AmazonServices.ItemSearch With search
.SubscriptionId = SubscriptionId
.Request = New AmazonServices.ItemSearchRequest() _
{searchRequest} End With ' Get Response
Dim response As AmazonServices.ItemSearchResponse Try
service.Timeout = 5000
response = service.ItemSearch(search) Catch End Try
If IsNothing(response) Then Return Nothing End If
Return response.Items(0).Item End Function '''
''' The Amazon Author property represents a list of authors.
''' Therefore, we create a comma separated list '''
Public Shared Function FormatAuthor(ByVal Authors As String()) _
As String If Not IsNothing(Authors) Then
Return String.Join(", ", Authors) Else
Return "Not Listed" End If End Function '''
''' Formats Amazon ListPrice into US currency '''
Public Shared Function FormatPrice(ByVal Price As String) As String
If Not IsNothing(Price) Then
Return "$" & Price.Insert(Price.Length - 2, ".") Else
Return "Not Listed" End If End Function '''
''' Formats tooltip for the link to the book details '''
Public Shared Function _
FormatDetailsTooltip(ByVal Title As String) As String
If Not IsNothing(Title) Then
Return String.Format("Link to {0} details", Title) Else
Return "Link to details" End If End Function '''
''' If there is no book cover, we fall back to displaying our image
''' Public Shared Function FormatBookCover(ByVal Url As String) _
As String If Not IsNothing(Url) Then Return Url
Else Return "Images/NoBookCover.gif" End If
End Function End Class
该类中的两个最重要的函数名为 GetBooksFromAmazon 和 SearchBooksFromAmazon。第一个函数从 Default.aspx 页中调用,以便按照类别显示书籍清单。第二个函数从 Search.aspx 页中调用,以便使用户能够搜索书籍。
这两个函数都使用名为 AmazonServices 的 Web 服务代理类。该代理类是通过依次选择菜单选项 Web site、Add Web Reference 并且输入 URL http://soap.amazon.com/onca/soap?Service=AWSECommerceService 创建的。这是用于访问美国 Amazon 数据的正确的 URL。
默认页
Default.aspx 页显示书籍类别的列表,并且显示所选类别的匹配书籍的列表(参见图 14)。Default.aspx 页包含在清单 11 中。

图 14. 默认页
清单 11. Default.aspx
Title="Super Super Books" % Runat="Server"Book ListingsToolTip="Book categories menu"StaticMenuItemStyle-CssClass="menuNormal"StaticSelectedStyle-CssClass="menuSelected" Runat="server"Value="1" NavigateUrl="~/Default.aspx?index=0" /Value="2" NavigateUrl="~/Default.aspx?index=1" /Value="4" NavigateUrl="~/Default.aspx?index=2" /Value="5" NavigateUrl="~/Default.aspx?index=3" /Value="6" NavigateUrl="~/Default.aspx?index=4" /Value="25" NavigateUrl="~/Default.aspx?index=5" /
AutoGenerateColumns="false" CssClass="books"HeaderStyle-CssClass="booksHeader"EmptyDataText="No matching results" Runat="server"ImageUrl=''AlternateText="Book cover image"Runat="server" /
Authors:Price:Sales Rank:NavigateUrl=''Text="View Details" Tooltip=''Runat="server" /
SelectMethod="GetBooks" Runat="server"Name="CategoryId" ControlId="menuCategories"DefaultValue="1" /
该页使用下列两个 ASP.NET 控件来显示书籍清单:Menu 控件和 GridView 控件。Menu 控件用来显示书籍类别的列表,而 GridView 控件用来显示书籍列表。
GridView 控件被绑定到 ObjectDataSource 控件。ObjectDataSource 控件继而调用 Amazon 类中的 GetBooks() 方法来检索书籍列表。
默认页的 XHTML 功能
在生成 XHTML 页时,目标之一是将文档的结构与其表示形式截然分开。为了达到这一目标,不能在 Default.aspx 页中的任何 ASP.NET 控件上设置格式属性。页格式设置被封装在通过 ASP.NET 主题与该页相关联的外部样式表中。
ASP.NET 2.0 主题使您可以更容易地遵循 Web 标准,因为它们将所有表示内容与页分开。示例站点包含一个名为 SiteTheme 的主题,该主题包含单个样式表。该主题自动使用 Web.Config 文件中的下列配置设置与每个页相关联。
styleSheetTheme="SiteTheme" masterPageFile="SiteMaster.master" /
您应该注意到,HTML 表没有用来创建页布局。尽管 XHTML 标准和可访问性标准都没有禁止您使用表来创建页布局,但这两个标准都建议您避免这样做。在示例站点中,页布局完全是由外部样式表确定的。页本身由两个 元素划分为两列。外部样式表包含确定这两个 元素位置的规则。
最后,示例站点在提供页时使用内容协商。当使用能够理解 application/xhtml+xml MIME 类型的浏览器从 Web 站点请求页时,将以 MIME 类型提供该页;否则,将以 text/html 类型提供该页。
内容协商是用 Global.asax 文件中的以下事件处理程序完成的。Sub Application_PreSendRequestHeaders(ByVal s As Object, _
ByVal e As EventArgs) If Array.IndexOf(Request.AcceptTypes, _
"application/xhtml+xml") -1 Then
Response.ContentType = "application/xhtml+xml" End If End Sub
默认页的可访问性功能
当无法提供 JavaScript 的文本等效内容时,WCAG 和 508 节可访问性准则都禁用客户端 JavaScript。为了满足这些准则,Default.aspx 页不依赖于客户端 JavaScript。即使您在浏览器中关闭 JavaScript,该页仍然能够正常工作。
为了满足该要求,在实现菜单时必须完成额外的工作。默认情况下,ASP.NET Menu控件为每个菜单项呈现 JavaScript 以处理客户端单击事件。但是,当为菜单项提供了 NavigateUrl 属性时,该菜单项将不再使用 JavaScript。
在示例站点中,为每个菜单项提供了一个指回到 Default.aspx 页的 NavigateUrl 属性。当您单击菜单项时,将重新加载 Default.aspx 页。Page_Load 事件处理程序用来检测单击了哪个菜单项,而且该子例程用当前菜单选择更新菜单。
使用 Menu 控件的好处是,Menu 控件自动生成“跳过导航”链接。如果使用 Tab 键浏览 Default.aspx 页中的每个元素,会注意到(观察浏览器的状态栏)有一个跳过菜单内容的隐藏链接。Menu 控件使您能够自动满足 WCAG 和 508 节准则,该准则要求您提供相应的方法以跳过重复性的导航链接。
搜索页
搜索页包含一个表单,使 Web 站点的用户能够通过提供书籍作者、书名、书籍关键字或者通过提供复杂查询来搜索书籍(参见图 15)。查询的结果显示在 GridView 控件中。Search.aspx 页包含在清单 12 中。

图 15. 搜索页
清单 12. Search.aspx
Title="Search Books" %
Runat="Server"Search BooksQuick SearchAssociatedControlID="txtAuthor" AccessKey="a"Runat="server" /ToolTip="Search by author" Runat="server" /access key is aText="Title:" AssociatedControlID="txtTitle"AccessKey="t" Runat="server" /id="txtTitle" ToolTip="Search by title"Runat="server" /access key is tAssociatedControlID="txtKeywords" AccessKey="k"Runat="server" /ToolTip="Search by keywords" Runat="server" /access key is kid="btnQuickSearch" Text="Quick Search Now"ToolTip="Peform quick search" AccessKey="s"Runat="server" OnClick="btnQuickSearch_Click" /access key is sPower SearchAssociatedControlID="txtPowerSearch" AccessKey="q"Runat="server" /ToolTip="Power search query text" TextMode="MultiLine"Columns="20" Rows="3" Runat="server" /access key is qid="btnPowerSearch" Text="Power Search Now"ToolTip="Perform power search" AccessKey="p"Runat="server" OnClick="btnPowerSearch_Click" /access key is pDataSourceID="srcBooks" AutoGenerateColumns="false"CssClass="books" HeaderStyle-CssClass="booksHeader"EmptyDataText="No matching results" Runat="server"ImageUrl=''AlternateText="Book cover image"Runat="server" /
Authors:Price:Eval("ItemAttributes.ListPrice.Amount"))%Sales Rank:NavigateUrl=''Text="View Details" Tooltip=''Runat="server" /
SelectMethod="SearchBooksFromAmazon" Runat="server"Name="Author" ControlId="txtAuthor"ConvertEmptyStringToNull="true" /ControlId="txtTitle"ConvertEmptyStringToNull="true" /ControlId="txtKeywords"ConvertEmptyStringToNull="true" /ControlId="txtPowerSearch"ConvertEmptyStringToNull="true" /
搜索页的 XHTML 功能
就像默认页一样,搜索页不包含任何表示性元素或属性。搜索页的样式和布局完全封装在通过 ASP.NET 主题与该页相关联的外部样式表中。
同样,像默认页一样,搜索页使用内容协商。如果有人用能够识别 application/xhtml+xml MIME 类型的浏览器请求搜索页,则将以该 MIME 类型提供该页;否则,将以 text/html 类型提供该页。
搜索页的可访问性功能
搜索页包含一个表单。或者,更准确地说,该页包含被划分为两个子表单的单个表单。它包含一个“Quick Search”表单和一个“Power Search”表单。
请注意,该表单用 HTML 标记划分为两个子表单。 标记使您能够将逻辑相关的表单元素组合在一起。可访问性准则要求您在处理复杂表单时使用 标记(请参阅 WCAG 12.3)。
而且,请注意,每个表单字段都与其标签显式关联。每个 ASP.NET 控件都包含一个指向其相应表单字段的 AssociatedControlID 属性。标签和字段之间的这些显式关联可以帮助屏幕阅读器的用户确定特定表单字段的用途。
最后,请注意每个 Label 控件都分配了一个访问键。访问键使您无需使用鼠标就能够方便地浏览表单字段。例如,如果按 ALT+A,则可以输入作者的姓名。如果随后按 ALT+S,则会提交“Quick Search”表单,并且在 GridView 中显示结果。换句话说,无需触摸鼠标即可方便地执行搜索。
如果按 ALT 键,则会自动显示访问键(参见图 16)。这通过 JavaScript 实现。请注意,在每个表单字段后面都会出现一个 标记。例如,Title 搜索字段是通过以下代码实现的。Text="Title:" AssociatedControlID="txtTitle"AccessKey="t" Runat="server" /id="txtTitle" ToolTip="Search by title"Runat="server" /access key is t当按 ALT 键时,将执行客户端 JavaScript,并且显示标记的内容。


title="Explanation of XHTML 1.0 Conformance"src="http://www.w3.org/Icons/valid-xhtml10"alt="Valid XHTML 1.0 icon" class="icon"/
title="Explanation of CSS Conformance"src="http://jigsaw.w3.org/css-validator/images/vcss"alt="Valid CSS icon" class="icon" /
title="Explanation of Level Double-A Conformance"src="http://www.w3.org/WAI/wcag1AA"alt="Level Double-A conformance icon,W3C-WAI Web Content Accessibility Guidelines 1.0"class="icon" /
母版页的 XHTML 功能
利用母版页,可以方便地为 Web 站点中的所有页提供正确的 DOCTYPE。SiteMaster.master 页包含 XHTML 1.0 Strict DOCTYPE。在母版页中指定 DOCTYPE 的好处是:如果您将来需要更改 Web 站点中所有页的 DOCTYPE,那么只需在一个位置更改它。例如,在不久之后的某一天,您可能希望迁移到 XHTML 1.1 Web 站点并修改 DOCTYPE 以反映所做的更改。
母版页还包含一系列一致性标识语,它们出现在示例 Web 站点中每一页的页脚。一致性标识语通告人们该 Web 站点符合 XHTML、CSS 和 WCAG 1.0 Web 标准(参见图 18)。

图 18. 一致性标识语母版页的可访问性功能
示例 Web 站点中每一页的顶部都包含一个链接,可以使用该链接切换用于显示相应页的样式表。每一页都可以用下列两个版本之一进行显示:“普通文本”版本和“大型文本”版本。在选择“大型文本”版本以后,Web 站点中所有文本的大小都会有所增加以使可读性更强(参见图 19)。

图 19. 大型文本大小
用户只需要执行该选择一次。如果某个用户选择 Web 站点的“大型文本”版本,那么该首选项会被自动记录下来,并且每当该用户返回该 Web 站点时都会使用它。该功能是通过利用 ASP.NET 2.0 框架的另一项新功能实现的:配置文件。配置文件使您能够存储用户在多次访问 Web 站点时的设置。
配置文件在 Web.Config 文件中定义。name="AccessibleStyleSheet" type="Boolean"defaultValue="false" /该配置文件定义一个名为 AccessibleStyleSheet 的布尔型属性。在 Web.Config 文件中定义该属性以后,就可以在任何 ASP.NET 页中,通过由 Page 类公开的 Profile 属性来读取或设置该属性。例如,要将 AccessibleStyleSheet 属性设置为值 True,并且显示该 Web 站点的“大型文本”版本,可以编写以下代码。Profile.AccessibleStyleSheet = true母版页包含用于选择 Web 站点的“普通文本”或“大型文本”版本的所有逻辑。HyperLink 控件用于使 Web 站点访问者能够执行该选择。在单击 HyperLink 以后,Page_Load 事件处理程序检测用户的选择并设置 AccessibleStyleSheet Profile 属性。如果使用 LinkButton 控件(而不是 HyperLink 控件),那么这里的代码会更加简单。但是,可访问性准则再一次禁止我们这样做,因为 LinkButton 控件依赖客户端 JavaScript。在选择“大型文本”版本以后,将向该页中添加对附加样式表的引用。该样式表包含单个规则。body {font-size: x-large; }该规则将正文字体大小设置为值 x-large。因为主样式表(包含在 SiteTheme 文件夹中)中指定的所有字体都使用相对大小,所以修改正文元素的字体大小会自动增加 Web 站点中所有元素的字体大小。小结Web 标准是一个好东西。通过遵循 Web 标准,您能够以最少的工作,让最广大的受众访问您的 Web 站点。您的 Web 站点将与更多的浏览器兼容,并且它们更有可能在将来继续正常工作。ASP.NET 2.0 框架旨在使您能够轻松地生成满足 Web 标准的 Web 站点。该框架使您能够轻松地生成 XHTML Web 站点。在 ASP.NET 2.0 框架中,默认情况下,所有 ASP.NET 控件都呈现 XHTML 元素和属性。而且,Visual Studio .NET 2005 和 Visual Web Developer 允许您在生成页的过程中自动针对 XHTML 标准验证这些页的有效性。通过 ASP.NET 2.0 框架,还可以更容易地生成可被残疾人士访问的 Web 站点。ASP.NET 2.0 框架中的控件包含大量在设计时考虑了可访问性的新属性。例如,每个呈现图像的 ASP.NET 控件都使您能够呈现图像的替换文本。此外,所有新导航控件都包含“跳过导航”链接,以使残疾人士可以更容易地浏览 Web 站点。