就像在“提出问题”部分所提到的,需要找到一些方法找到方法能够来创建一个易于维护并且让用户容易理解的菜单系统。您可能会想只要硬编码一个HTML菜单就可以了,但这并不是个好的选择,因为如果想在多个地方都放置菜单,(((在本网站,是在页头和页脚中放置菜单))),或者当想要在菜单中添加和修改链接时,都需要对代码进行复制和粘贴。在本书第1版中开发了一个自定义控件,该控件使用了一个包含网站地图的XML文件,并且通过对它应用一个XSL 文件来构建要显示的生成在页面上的HTML。ASP.NET 2.0引入了一些新的内置控件和功能,可以实现几乎相同的功能,但提供了更多函数,使开发者开发人员的工作变得更简单。
1. 定义网站地图文件
菜单项是指定定义在一个XML网站地图文件中的。整个网站的主网站地图文件名为web.sitemap,该其中有文件中包含一个
在本书示例中,要有一级节点,如Home、Contacts 和About,以及二级节点,甚至可能还有三级节点,如Store/Shopping Cart。这里下面是一个更复杂的示例,显示了一些二级菜单项条目,它还引用了另一个子文件子siteMap,为Store提供菜单项:
子siteMap((StoreMenu.sitemap))有与web.sitemap的相同的格式,它并且有一个外部节点(((outer)))siteMap。
2. 将Menu控件同SiteMap绑定
定义好了sitemap文件后,就可以将其作为像Menu 和TreeView 这样的等ASP.NET 2.0新控件的数据源。ASP.NET 2.0还引入了新的非可视控件——DataSource,它可以连接到一个数据库、XML文件,或者组件类。图形控件使用这些控件来被图形控件使用来获得要绑定的数据并且显示在屏幕上。实际上,它们作为担当了实际的数据存储和可视化控件之间的一个桥梁。SiteMapDataSource就是这些DataSource 控件之一,尤其它是专门为网站的web.sitemap 文件设计的。就像这样定义方式如下:
注意,没有定义web.sitemap 文件的路径,因为整个网站只能有一个网站地图,且名称为~/web.sitemap。当为站点的网站中的有限个子文件夹创建子网站地图时,会用绑定被明确地((transparently))显式的方式来处理绑定,因为它们是从web.sitemap被链接的。
注意:
如果不喜欢默认的SiteMap DataSource控件的工作方式(((因为您可能想支持多sitemap 文件,或者想将网站地图保存在数据库中而不是在XML文件中))),就需要一个DataSource 控件;或者写编写一个新的DataSource 控件;或者创建一个提供程序类(provider class)明确显式地为SiteMapDataSource提供内容。
该菜单控件可以创建流行的DHTML fly-out菜单,能以垂直方向或者水平方向显示。在ASP.NET 1.1中没有标准的Menu控件并且似乎每个Web组件开发商都会提供他们自己的组件来创建创建这些菜单。然而在ASP.NET 2.0中内置了一个免费的标准的菜单控件,它可以整合不同的数据源控件。为了要创建一个Menu控件,并绑定到同上面定义的SiteMapDataSource上绑定,可以简单地用定义如下方式定义:
当然,Menu 控件还提供了许多属性:允许指定设置它它显示的方向(((Orientation属性)))、要使用的CSS类(((CssClass))),或者它不同部分的外观、内部网站地图显示在fly-out菜单中的层数(((StaticDisplayLevels)))等等。然而,完整地对这些属性进行介绍则超过了本书并的范围不能完整地介绍这些属性,可以请参考官方的MSDN文档来查看详细信息。
如果用树型视图(一个tree view)显示网站地图而不用替换fly-out菜单来显示网站地图,则只需要将声明的Menu用TreeView控件代替,如下所示:
3. Breadcrumbs
除了显示菜单,还要为用户提供了一个显示当前页的位置他现在哪里的可视化提示信息,并显示返回到主页的路径链接。添加一些方法来允许用户能从他当前所在位置向前导航到上任意上一步页面上。这通常是由breadcrumbs完成的,即一个导航栏,已显示了到所有页面(或区域)或者区域的链接的导航栏,从主页开始,然后是用户浏览过的页面直到到达当前页面,如下所示:
Home > Store > Shopping cart
通过导航系统,用户可以回退两个页面而不需要两次点击浏览器的“后退”按钮两次(((有时由于某些原因该按钮是不可见的))),也不需要重回到主页,并记住以前访问页面的顺序。在ASP.NET 2.0,中通过在页面上声明一个新的SiteMapPath控件实例,只用一行代码就可以添加一个breadcrumb栏:
此该控件照样有可以通过一些属性允许对它的外观完全自定义,您可以在“解决方案”部分看到实际示例。
2.2.8 创建一个可访问的网站
默认情况下,所有ASP.NET 2.0内置标准控件生成的是格式良好的XHTML1.0过渡代码。XHTML代码是基于XML的HTML,同HTML相比有更严格的格式,例如,所有的属性值必须位于双引号内,所有的标记必须有一个关闭符,或者是显式地自封闭(((例如,
和应写为
和))),并且内嵌的顺序必须用右尖括号引起来正确(((例如,
hello Marco
应写为Hello Marco
)))。另外,许多HTML标记用来定义文本格式,例如、W3C定义了一套规则,使使有生理障碍的用户也可以轻松地浏览网站,无障碍网页内容可及性访问规范1.0(((Web Content Accessibility Guidelines 1.0,WCAG)))的官方网站为www.w3.org/TR/WCAG10/,美国联邦机构网站所必须遵守的Section 508 规范就来源于WCAG,其详细信息可以访问www.section508.gov。例如,在 标签中必须使用alt属性为弱视者提供可替换文本,必须在输入框旁配合使用
如果想读到更多关于XHTML、可及性可访问性,以及ASP.NET 2.0中关于此类主题的新功能,可以参考下列免费的在线文章:Alex Homer的Accessibility Improvements in ASP.NET 2.0-Part 1(((www.15seconds.com/issue/040727.htm))),、Accessibility Improvements in ASP.NET 2.0-Part 2(((www.15seconds.com/issue/040804.htm))),或者Stephen Walther的Building ASP.NET 2.0 Web Sites Using Web Standards(((http://msdn.microsoft.com/ASP.NET/default.aspx?pull=/library/ en-us/dnaspp/html/aspnetusstan.asp)))。
2.2.9 在所有页面上共享公共通用行为
母页模板页面和主题能够使网站上所有页面共享设计和外观,不过然而,有一些通用行为可能也需要共享。也就是说在页面生命周期的某个点上所运行的代码需要共享。例如:为了能够显示网站的统计信息,需要要对所有页面记录日志,这需于是就要在页面加载时执行一些代码。另一个示例是需要在页面PreInit事件处理程序中添加设置Theme属性的代码。可以将这些共用的代码放在到一个外部函数中,然后在每个页面上只需要添加一行代码来调用它即可。但这个方法存在两个缺点:
q
● 在每设计一个新页面时,都时千万不要忘记需要添加一行代码来调用那些外部函数。如果是多人开发,需要保证每个人每个开发人员都能记住记得。
q
● 如果在页面的PreInit 事件中运行一些初始化代码,在Load 事件中运行其他其他代码,就必须写成编写两个xxxInitialize 方法,并要从在正确的事件处理程序中调用正确的方法,。就要为每个页面添加更多的代码。因此,不要认为在每个页面上添加一行代码很容易,因为以后需要添加的代码越来越多。如果页面有上百个,那么这个方法是不可行的。
因为这两个劣势缺点,足以让所以我们放弃此方法,另一个方法是把共用通用的代码写在母页模板页面的后台代码中。在很多情况下这都是个很好的选择,而不仅仅是在本书的示例本例中。但是,因为必须处理PreInit 事件,而MasterPage 类(((以及它的基类)))却没有该事件,它只能处理Init 或者 Load 事件却不能处理PreInit等事件,所以必须考虑其他其他东西方式。
本书上一个版本中有一个BasePage类,所有的内容页面都继承于它,而不是直接从标准的System.Web.UI.Page类继承,可以相信认为这仍然是最好的选择。因为可以通过重写在该类中的可以通过覆盖OnXXX方法来处理页面上的任何事件(((XXX是事件名称)))。
下面的片段展示了一个自定义基类的框架。它继承于Page类,重写覆盖了OnPreInit 和 OnLoad 方法:
public class BasePage : System.Web.UI.Page
{
protected override void OnPreInit(EventArgs e)
{
// add custom code here...
base.OnPreInit(e);
}
protected override void OnLoad(EventArgs e)
{
// add custom code here...
base.OnLoad(e);
}
}
页面的后台代码文件中的类会继承于这个自定义基类,而不是继承于标准的Page类,如下所示:
public partial class Contact : BasePage
{
// normal page code here...
}
在每个页面的隐藏代码后台代码类中还需要修改很多一些代码,但只要但完成了这些修改以后,若对以后仅需在BasePage中的对已有的方法进行代码添加或者重载就可以了,页面的隐藏代码不需要在后台代码类中会继承BasePage中的改动而不需要做任何新的修改修改,因为它们继承了BasePage中的改动。如果在一开始就采用该此方法,则那么只需要在创建页面时对其code-beside隐藏代码类进行修改即可。