母版页与网站导航

本文档是 Visual C# 教程 (切换到 Visual Basic 教程)

用户友好网站的一个共同特征是其具有一致的站点级页面布局和导航模式。此教程介绍了如何为所有网页创建一致的外观,并轻松地进行更新。

« 前一篇教程  |  下一篇教程 »

Part 2

步骤 3:根据站点地图显示菜单

ASP.NET 2.0 中的数据访问可以通过编码实现 ( 与 ASP.NET 1.x 版本类似 ), 也可以通过新的数据源控件 以声明方式实现。这些内置的数据源控件包括 SqlDataSource 控件(用于访问关系数据库数据)和 ObjectDataSource 控件(用于访问各个类的数据)等等。用户甚至可以创建自己的自定义数据源控件

数据源控件用作 ASP.NET 页面与底层数据间的代理。为显示数据源控件获得的数据,通常,我们会再为页面添加一个 Web 控件,并将该控件绑定到数据源控件。要将一个 Web 控件绑定到数据源控件,只需将 Web 控件的 DataSourceID 属性设为数据源控件 ID 属性的值。

为简化站点地图数据的使用,ASP.NET 中包含了 SiteMapDataSource 控件, 该控件允许我们将 Web 控件绑定到网站的映射。 TreeView 和 Menu 这两个 Web 控件通常用于提供导航用户界面。要将站点地图数据绑定到 TreeView (或 Menu )控件,只需将一个 SiteMapDataSource 与 TreeView (或 Menu )控件一起添加到页面,系统会对 TreeView (或 Menu )控件的 DataSourceID 属性进行相应的设置。例如,我们可以使用以下标记将一个 Menu 控件添加到母版页:

<div id="navigation">
    <asp:Menu ID="Menu1" runat="server"
      DataSourceID="SiteMapDataSource1">
    </asp:Menu>
    <asp:SiteMapDataSource ID="SiteMapDataSource1" runat="server" />
</div>

为了界面的美化,我们可以将SiteMapDataSource 控件绑定到Repeater 控件 , 如下所示 :

<div id="navigation">
    <ul>
        <li><asp:HyperLink runat="server" ID="lnkHome"
         NavigateUrl="~/Default.aspx">Home</asp:HyperLink></li>
        <asp:Repeater runat="server" ID="menu"
          DataSourceID="SiteMapDataSource1">
            <ItemTemplate>
                <li>
                    <asp:HyperLink runat="server"
                    NavigateUrl='<%# Eval("Url") %>'>
                    <%# Eval("Title") %></asp:HyperLink>
                </li>
            </ItemTemplate>
        </asp:Repeater>
    </ul>
    <asp:SiteMapDataSource ID="SiteMapDataSource1"
      runat="server" ShowStartingNode="false" />
</div>

SiteMapDataSource 控件每次返回一级站点的地图层次结构,首先是站点地图的根节点 ( 在我们的站点地图中是 Home ),然后是下一个级别(Basic Reporting 、Filtering Reports 和Customized Formatting ),以此类推。在将 SiteMapDataSource 与 Repeater 绑定时,它会枚举返回的第一个级别,并将该级别中每个 SiteMapNode 实例 映射到 ItemTemplate 实例。要访问 SiteMapNode 的一个特定属性,我们可以使用 Eval(propertyName) 来获得每个 SiteMapNode 的 Url 和 Title 属性,以便进行超链接控制。

上述 Repeater 示例将呈现以下标记 :

<li>
    <a href="/Code/BasicReporting/Default.aspx">Basic Reporting</a>
</li>
<li>
    <a href="/Code/Filtering/Default.aspx">Filtering Reports</a>
</li>
<li>
    <a href="/Code/CustomFormatting/Default.aspx">
     Customized Formatting</a>
</li>

这些站点地图节点 (Basic Reporting 、Filtering Reports 和Customized Formatting ) 呈现的是站点地图的第二个 ( 而非第一个 ) 级别。这是因为, SiteMapDataSource 的 ShowStartingNode 属性设置为 False ,导致 SiteMapDataSource 绕过站点地图的根节点,转而开始返回第二个级别的站点地图层次结构。

要显示 Basic Reporting 、Filtering Reports 和 Customized Formatting SiteMapNodes 的子节点,我们可以为初始Repeater 的 ItemTemplate 添加另一个 Repeater 控件 。第二个 Repeater 控件 将绑定到 SiteMapNode 实例的 ChildNodes 属性,如下所示:

<asp:Repeater runat="server" ID="menu" DataSourceID="SiteMapDataSource1">
    <ItemTemplate>
        <li>
            <asp:HyperLink runat="server"
             NavigateUrl='<%# Eval("Url") %>'>
             <%# Eval("Title") %></asp:HyperLink>
            <asp:Repeater runat="server"
                DataSource='<%# ((SiteMapNode) Container.DataItem).ChildNodes %>'>
                <HeaderTemplate>
                    <ul>
                </HeaderTemplate>
                <ItemTemplate>
                    <li>
                        <asp:HyperLink runat="server"
                         NavigateUrl='<%# Eval("Url") %>'>
                         <%# Eval("Title") %></asp:HyperLink>
                    </li>
                </ItemTemplate>
                <FooterTemplate>
                    </ul>
                </FooterTemplate>
            </asp:Repeater>
        </li>
    </ItemTemplate>
</asp:Repeater>

这两个Repeater 导致系统显示以下标记( 为了体现简洁性 , 我们删除了一些标记 ):

<li>
    <a href="/Code/BasicReporting/Default.aspx">Basic Reporting</a>
    <ul>
       <li>
          <a href="/Code/BasicReporting/SimpleDisplay.aspx">
            Simple Display</a>
       </li>
       <li>
          <a href="/Code/BasicReporting/DeclarativeParams.aspx">
            Declarative Parameters</a>
       </li>
       <li>
          <a href="/Code/BasicReporting/ProgrammaticParams.aspx">
            Setting Parameter Values</a>
       </li>
    </ul>
</li>
<li>
    <a href="/Code/Filtering/Default.aspx">Filtering Reports</a>
    ...
</li>
<li>
    <a href="/Code/CustomFormatting/Default.aspx">
      Customized Formatting</a>
    ...
</li>

通过使用从The CSS Anthology: 101 Essential Tips, Tricks, & Hacks ( 作者 :Rachel Andrew )中选择的 CSS 样式,<ul> 和 <li> 元素经过样式化,因此标记将产生以下可视化输出 :

图11 :由两个 Repeater 和一些 CSS 构成的菜单

此菜单位于母版页中,并绑定到在Web.sitemap 中定义的站点地图,即,站点地图的任何更改会立即反应到使用Site.master 母版页的所有页面。

禁用ViewState

所有ASP.NET 控件都可以选择将其状态保存到视图状态 ,此视图状态在呈现的HTML 中序列化为隐藏的表单域。控件可以使用视图状态记住在页面返回时通过编程更改的状态,如绑定到一个数据 Web 控件的数据。当视图状态允许控件记住页面返回时的信息时,它将增加向客户端发送的标记的大小。如果没有进行严密监控,这将导致严重的页面膨胀。数据 Web 控件(尤其是 GridView )会向页面增加大量额外标记。当然,这些数据增加可能对宽带用户或企业内部网用户影响不大,但视图状态会给拨号上网的用户增加几秒钟的延迟时间。

要观察视图状态的影响,在浏览器中打开一个页面,然后查看网页发送的源代码( 在Internet Explorer 中,单击 View 菜单,选择 Source 选项 )。你还可以打开页面跟踪 选项,查看该页面上每个控件的视图状态配置。视图状态信息经序列化后存放在名为 __VIEWSTATE 的隐藏表单域中,该字段位于 <form> 开始标记后的 <div> 元素中。仅当使用 Web 窗体时,视图状态才可以被保存;如果 ASP.NET 页面的声明语法中不包含 <form runat="server"> ,呈现的标记中不会出现 __VIEWSTATE 隐藏表单字段。

母版页生成的__VIEWSTATE 表单域会向页面生成的标记添加大约 1,800 个字节。这些额外的数据增加主要是 SiteMapDataSource 控件的内容保存到视图状态时由 Repeater 控件产生的。尽管 1,800 个字节看起来并不多,但当使用带有多个字段和记录的 GridView 时,视图状态很容易膨胀 10 倍或更多。

通过将EnableViewState 属性设置为 false ,用户可以在页面或控件级禁用视图属性,从而减小所呈现标记的大小。由于数据 Web 控件的视图状态可以保存页面返回绑定到数据 Web 控件的数据,在禁用数据 Web 控件的视图状态时,必须在每次页面返回时重新绑定数据。在 ASP.NET 1.x 版本中,这个责任落在页面开发人员身上;然而,在 ASP.NET 2.0 中,页面返回时,数据 Web 控件会在需要时重新绑定到数据源控件。

要减少页面的视图状态,我们可以将 Repeater 控件的 EnableViewState 属性设为 false 。此操作可以在设计器的 Properties 窗口中完成,也可以在 Source 视图中以声明方式完成。完成此修改后,Repeater 的声明标记应显示为:

 

 

<asp:Repeater runat="server" ID="menu" DataSourceID="SiteMapDataSource1"
    EnableViewState="False">
    <ItemTemplate>
        ... <i>ItemTemplate contents omitted for brevity</i> ...
    </ItemTemplate>
</asp:Repeater>

经过此修改后,该页面所呈现的视图状态的大小缩减到52 个字节,减少了 97% 的视图状态数据!在该系列的教程中,我们会关闭数据 Web 控件的视图状态,以便减少所呈现标记的大小。在大多数示例中,在没有提示的情况下, EnableViewState 属性将设置为 false 。只有当数据 Web 控件必须启用视图状态才能提供所期望的功能时,我们才会讨论视图状态。

步骤4 :添加 Breadcrumb 导航

为完成母版页,我们需要为每个页面添加一个 breadcrumb 导航 UI 元素。Breadcrumb 导航会快速显示用户当前在网站层次结构中的位置。在 ASP.NET 2.0 中添加 breadcrumb 很简单,仅需向页面添加一个 SiteMapPath 控件;无需编码。

在我们的网站中,将此控件添加到头部的 <div> :

<span class="breadcrumb">
    <asp:SiteMapPath ID="SiteMapPath1" runat="server">
    </asp:SiteMapPath>
</span>

Breadcrumb 显示用户在站点地图层次结构中访问的当前页面,以及站点地图节点的 “ 上级节点 ”,直至根节点( 在我们的站点地图中是 Home )。

图12 :Breadcrumb 显示在站点地图层次结构中的当前页及其上层节点

步骤5 :为每个部分添加默认页面

在我们的网站中,教程细分为Basic Reporting 、Filtering 、Custom Formatting 等分类,每个分类拥有一个文件夹,该文件夹中有对应课程的 ASP.NET 页面。此外,每个文件夹中还包含一个 Default.aspx 页面。此默认页面将显示当前部分的所有教程。例如,在 BasicReporting 文件夹的 Default.aspx 中,我们可以链接到 SimpleDisplay.aspx 、 DeclarativeParams.aspx 和 ProgrammaticParams.aspx 。在此处,我们可以再次使用 SiteMap 类和数据 Web 控件显示 Web.sitemap 中定义的站点地图的信息。

让我们再次使用 Repeater 显示一个无序列表,但这一次,我们会显示教程的标题和说明。由于我们需要在每个 Default.aspx 页面重复完成此操作需要的标记和代码,因此我们可以将此 UI 逻辑封装成一个用户控件 。在网站中创建一个名为 UserControls 的文件夹,在其中添加一种类型为 Web 用户控件的新条目,条目名为 SectionLevelTutorialListing.ascx ,包含以下标记:

图13 :向 UserControls 文件夹添加新 Web 用户控件Folder

SectionLevelTutorialListing.ascx

<%@ Control Language="CS" AutoEventWireup="true"
    CodeFile="SectionLevelTutorialListing.ascx.cs"
    Inherits="UserControls_SectionLevelTutorialListing" %>
<asp:Repeater ID="TutorialList" runat="server" EnableViewState="False">
    <HeaderTemplate><ul></HeaderTemplate>
    <ItemTemplate>
        <li><asp:HyperLink runat="server"
         NavigateUrl='<%# Eval("Url") %>'
         Text='<%# Eval("Title") %>'></asp:HyperLink>
                - <%# Eval("Description") %></li>
    </ItemTemplate>
    <FooterTemplate></ul></FooterTemplate>
</asp:Repeater>

SectionLevelTutorialListing.ascx.cs

using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
public partial class UserControls_SectionLevelTutorialListing : UserControl
{
    protected void Page_Load(object sender, EventArgs e)
    {
        // If SiteMap.CurrentNode is not null,
        // bind CurrentNode ChildNodes to the GridView
        if (SiteMap.CurrentNode != null)
        {
            TutorialList.DataSource = SiteMap.CurrentNode.ChildNodes;
            TutorialList.DataBind();
        }
    }
}

在前面的Repeater 示例中,我们以声明方式将 SiteMap 数据绑定到 Repeater ;然而,在此 SectionLevelTutorialListing 用户控件示例中,我们通过编程方式实现。在 Page_Load 事件处理程序中,将通过检测确保该页面的 URL 映射到站点地图中的一个节点。如果使用此用户控件的页面没有对应的 <siteMapNode> 条目, SiteMap.CurrentNode 将返回空值,没有数据绑定到 Repeater 。假设存在一个 CurrentNode ,我们将其子节点集绑定到 Repeater 。由于我们已经建立了站点地图,因此每个部分中的 Default.aspx 页面是该部分中所有教程的父节点,此节点将显示该部分所有教程的链接与说明,如下方屏幕截图所示。

一旦此Repeater 完成创建后,打开每个文件夹中的 Default.aspx 页面,转至 Design 视图,并将用户控件从 Solution Explorer 拖放到希望教程列表在其中显示的 Design 界面。

图14 :已将用户控件添加到 Default.aspx

图15 :Basic Reporting 教程列表

小结

完成站点地图和母版页后,现在,我们的数据相关教程拥有了一致的页面布局和导航模式。无论我们为网站添加了多少个页面,由于站点级页面布局与网站导航信息是集中管理的,因此更新这些信息是一个轻松快捷的过程。具体来说,在本教程中,页面布局信息和站点地图分别在 Site.master 母版页和 Web.sitemap 中定义。我们无需编写任何 代码就获得了站点级页面布局与导航机制。此外,我们仍在 Visual Studio 中提供完整的 WYSIWYG 设计器支持。

我们已经完成了数据访问层与业务逻辑层,并且定义了一致的页面布局和网站导航模式,接下来,我们将开始探讨常用报表模式。在接下来的三篇教程中,我们将讨论基本报表任务:在 GridView 、DetailsView 和 FormView 控件中显示从 BLL 获得的数据。

快乐编程!



上一页 | 1 | 2 | 下一页

下一篇教程