导言
通常,用户友好的个性化站点都有着一致的,站点统一的页面布局和导航体系。Asp.net 2.0引入的两个新特性给我们在统一站点的页面布局和站点导航上提供了简单而有效的工具,它们是母板页和站点导航。母板页允许开发者创建统一的站点模板和指定的可编辑区域。这样,aspx页面只需要给模板页中指定的可编辑区域提供填充内容就可以了,所有在母板页中定义的其他标记将出现在所有使用了该母板页的aspx页面中。这种模式允许开发者可以统一的管理和定义站点的页面布局,因此可以容易的得到拥有统一的视觉和感觉的页面并且还易于更新。
站点导航系统允许开发者定义站点地图并提供了API以便通过程序查询站点地图信息。新的导航控件包括Menu,TreeView和SiteMapPath,这样可以很容易的在一个一般的导航用户界面元素里呈现全部或者部分站点地图。我们将使用默认的站点导航提供者,这意味着我们的站点地图将定义在一个xml格式的文件中。
为说明这些观念并且使我们的教程的示例站点可用性更佳,让我们通过本次课程定义一个站点统一的页面布局,实现一个站点地图,并且添加导航UI。在这个课程结束时我们的课程示例站点就拥有一个优美的设计效果了。
图1:本课程的最终成果
步骤1:创建母板页
第一步是为我们的站点创建母板页。到目前为止我们的站点只有一个类型化的DataSet(Northwind.xsd,位于App_Code文件夹),业务逻辑层类库(ProductsBLL.cs,CategoriesBLL.cs等等,这些都在App_Code文件夹里),数据库(NORTHWIND.MDF,位于App_Data文件夹),配置文件(web.config),和一个CSS文件(Style.css)。
我整理这些页面和文件以说明前面两次课程中介绍的数据访问层和业务逻辑层将会在以后课程的更多细节中重用这些示例。
图2:我们项目中的文件
要创建一个母板页,用右键点击解决方案管理器中的项目名称并选择添加新项。然后从模板列表窗口中选择母板类型并且命名为Site.master
图3:添加一个母板页到站点中
在母板页中定义站点统一的页面布局。你可以用设计视图定义你需要的布局或者控件,你还可以手动的在代码视图中添加标记。在我们的母板页中使用了定义在外部文件Style.css中的
层叠样式表来定义位置和风格。也许你不知道下面这些标记怎样显示,样式表规则定义了导航用的<div>标签中的内容绝对定位在页面的左边并且宽度固定为200像素。
1
<%
@ Master Language="C#" AutoEventWireup="true" CodeFile="Site.master.cs" Inherits="Site"
%>
2
![](https://i-blog.csdnimg.cn/blog_migrate/635c746a47fe9fdafb7962bab037ffdb.gif)
3
<!
DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
>
4
![](https://i-blog.csdnimg.cn/blog_migrate/635c746a47fe9fdafb7962bab037ffdb.gif)
5
<
html
xmlns
="http://www.w3.org/1999/xhtml"
>
6
<
head
runat
="server"
>
7
<
title
>
Working with Data Tutorials
</
title
>
8
<
link
href
="Styles.css"
rel
="stylesheet"
type
="text/css"
/>
9
</
head
>
10
<
body
>
11
<
div
id
="wrapper"
>
12
![](https://i-blog.csdnimg.cn/blog_migrate/635c746a47fe9fdafb7962bab037ffdb.gif)
13
<
form
id
="form1"
runat
="server"
>
14
![](https://i-blog.csdnimg.cn/blog_migrate/635c746a47fe9fdafb7962bab037ffdb.gif)
15
<
div
id
="header"
>
16
<
span
class
="title"
>
Working with Data Tutorials
</
span
>
17
<
span
class
="breadcrumb"
>
18
TODO: Breadcrumb will go here
</
span
>
19
</
div
>
20
![](https://i-blog.csdnimg.cn/blog_migrate/635c746a47fe9fdafb7962bab037ffdb.gif)
21
<
div
id
="content"
>
22
<
asp:contentplaceholder
id
="MainContent"
23
runat
="server"
>
24
<!--
Page-specific content will go here
-->
25
</
asp:contentplaceholder
>
26
</
div
>
27
![](https://i-blog.csdnimg.cn/blog_migrate/635c746a47fe9fdafb7962bab037ffdb.gif)
28
<
div
id
="navigation"
>
29
TODO: Menu will go here![](https://i-blog.csdnimg.cn/blog_migrate/7b8419d166351d364d9f121b08f1aa8a.gif)
30
</
div
>
31
</
form
>
32
</
div
>
33
</
body
>
34
</
html
>
一个母板页定义了固定的布局和可以被那些使用了母板页的aspx页面填充的可编辑区域
这个可编辑区域是通过ContentPlaceHolder控件显示,位于<div>标记中。我们的母板页中只有一个ContentPlaceHolder(MainContent),但是母板页中是可以包含多个ContentPlaceHolder控件的。
输入上面的标记,切换到设计视图观察母板页的布局。所有的使用了这个母板页的aspx页面都会有这样统一的布局,而MainContent区域是留给aspx页面展现自己才华的地方。
图4:在设计视图中显示的母板页
步骤2:给站点添加一个主页
定义母板页后,我们准备给站点添加一些aspx页面。让我们从添加我们的首页Degault.aspx开始吧。在解决方案管理器中右键点击项目名称并且选择添加新建项目。从模板列表中选择Web Form选项并且命名为Default.aspx。并且,勾上“选择母板页”的复选框。
图5:添加一个新Web Form并且勾上“选择母板页”的复选框
点击确定按钮后,将会询问你新建的这个aspx页面使用哪个母板页。也许你有多个母板页在你的项目中,但是我们只有一个。
图6:选择你要使用的母板页
选择母板页后,新建的aspx会包含下面这些标记:
Default.aspx
1
<%
@ Page Language="C#" MasterPageFile="~/Site.master" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" Title="Untitled Page"
%>
2
<
asp:Content
ID
="Content1"
ContentPlaceHolderID
="MainContent"
3
Runat
="Server"
>
4
</
asp:Content
>
在@Page指令中有一个指向母板页的引用(MasterPageFile=”~/Site.master”),并且aspx页面的标记中包含了一个Content控件对应母板页中定义的ContentPlaceHolder控件,这个Content控件的ContentPlaceHolderID属性映射到指定的ContentPlaceHolder控件。你可以在Content控件中放置你想显示在相应ContentPlaceHolder控件位置的标记。
设置@Page指令的Title属性为Home并且添加一些欢迎词到Content控件中:
Default.aspx
1
<%
@ Page Language="C#" MasterPageFile="~/Site.master" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" Title="Home"
%>
2
<
asp:Content
ID
="Content1"
ContentPlaceHolderID
="MainContent"
3
Runat
="Server"
>
4
<
h1
>
Welcome to the Working with Data Tutorial Site
</
h1
>
5
![](https://i-blog.csdnimg.cn/blog_migrate/635c746a47fe9fdafb7962bab037ffdb.gif)
6
<
p
>
This site is being built as part of a set of tutorials that
7
illustrate some of the new data access and databinding features in
8
ASP.NET 2.0 and Visual Web Developer.
</
p
>
9
![](https://i-blog.csdnimg.cn/blog_migrate/635c746a47fe9fdafb7962bab037ffdb.gif)
10
<
p
>
Over time, it will include a host of samples that
11
demonstrate:
</
p
>
12
![](https://i-blog.csdnimg.cn/blog_migrate/635c746a47fe9fdafb7962bab037ffdb.gif)
13
<
ul
>
14
<
li
>
Building a DAL (data access layer),
</
li
>
15
<
li
>
Using strongly typed TableAdapters and DataTables
</
li
>
16
<
li
>
Master-Detail reports
</
li
>
17
<
li
>
Filtering
</
li
>
18
<
li
>
Paging,
</
li
>
19
<
li
>
Two-way databinding,
</
li
>
20
<
li
>
Editing,
</
li
>
21
<
li
>
Deleting,
</
li
>
22
<
li
>
Inserting,
</
li
>
23
<
li
>
Hierarchical data browsing,
</
li
>
24
<
li
>
Hierarchical drill-down,
</
li
>
25
<
li
>
Optimistic concurrency,
</
li
>
26
<
li
>
And more!
</
li
>
27
</
ul
>
28
</
asp:Content
>
@Page指令中的Title属性允许我们可以在aspx页面定义标题,即使母板页中已经定义了<title>元素。我们还可以使用Page.Title的编程方式设置页面的标题。需要注意的是母板页中引用的样式表(如Style.css)会自动校正以应用到每个aspx页面中,这是与aspx页面的目录和母板页目录之间的关系无关。
切换到设计视图我们会看到我们的页面将在浏览器中的显示效果。注意:在设计视图里,aspx页面的内容只有可编辑区域可以被修改,在母板页定义的非ContentPlaceHolder部分标记被显示成灰色。
图7:在设计视图中显示的可编辑区域及非可编辑区域
当Default.aspx页面被浏览器访问时,asp.net引擎会合并母板页的内容和aspx页的内容,并且将合并的内容呈现为最终的HTML发送到浏览器。当母板页的内容被更新,所有使用了这个母板页的aspx页面会在下次被请求时重新和新的母板页内容合并。简单的说,母板页模型允许定义一个统一的布局模板(母板页),当它改变时整个站点会反应这种改变。
添加更多的页面到站点中
让我们花一点时间添加另外的页面到站点中,以便支持最终的各种各样的课程的示例。这里总共会有超过35个示例,所以我们先创建一部分。以后会有很多类别的示例,为了更好的管理这些示例我们给每个分类添加一个文件夹。现在我们添加三个文件夹:
• BasicReporting
• Filtering
• CustomFormatting
最后,如图8所示向解决方案管理器中添加新文件。每添加一个文件的时候记住要勾上“选择母板页”的复选框。
图8:添加下列文件
第三步:添加站点地图
管理一个由大量网页组成的网站的其中一个挑战是要为访问者浏览网站提供一个捷径。作为开始,站点的导航结构必须被定义。下一步,这个结构必须转换成适于导航的用户界面元素,比如菜单或者位置导航。当有新页面添加到站点和已有的页面被移除的时候这个过程将要修改和校正。
在asp.net 2.0以前,开发者需要自己创建站点导航结构,维护它并且将它转化为适于导航的用户界面元素。在asp.net 2.0里,开发者可以利用非常灵活的且内置的站点导航系统。Asp.net 2.0站点导航系统允许开发者定义一个站点地图并且提供了可以访问这些信息的API。
默认的Asp.net站点地图提供者期望站点地图信息存储在xml格式的文件中。但是,建立在
提供者模型上的站点导航系统是可以被扩展的以支持多种方式储存的站点地图。Jeff Prosise的文章,
The SQL Site Map Provider You’ve Been Waiting For展示了怎样创建将站点地图存储在SQL Server数据库里的提供者;另外一个选择是
基于文件系统的站点地图提供者。
在这个指南中,我们仍然使用ASP.NET2.0里默认的站点地图提供者。要创建站点地图,在解决方案管理器里右键点击项目名称,选择添加新项,然后选择站点地图类型。命名为Web.sitemap然后单击添加按钮。
图9:向你的项目中添加站点地图
站点地图文件是一个xml文件。注意:Visual Studio可以为站点地图结构提供智能感知。站点地图文件必须含有<siteMap>作为根节点,它必须至少含有一个<siteMapNode>子节点。这个<siteMapNode>元素又可以包含任意数量的<siteMapNode>子元素。
站点地图模拟了文件系统。为每个文件夹添加一个<siteMapNode>元素,并且为每个aspx页面添加一个<siteMapNode>子元素,如此:
Web.sitemap:
1
<?
xml version="1.0" encoding="utf-8"
?>
2
<
siteMap
xmlns
="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0"
>
3
![](https://i-blog.csdnimg.cn/blog_migrate/635c746a47fe9fdafb7962bab037ffdb.gif)
4
<
siteMapNode
url
="~/Default.aspx"
title
="Home"
description
="Home"
>
5
<
siteMapNode
title
="Basic Reporting"
6
url
="~/BasicReporting/Default.aspx"
7
description
="Basic Reporting Samples"
>
8
<
siteMapNode
url
="~/BasicReporting/SimpleDisplay.aspx"
9
title
="Simple Display"
10
description
="Displays the complete contents
11
of a database table."
/>
12
<
siteMapNode
url
="~/BasicReporting/DeclarativeParams.aspx"
13
title
="Declarative Parameters"
14
description
="Displays a subset of the contents
15
of a database table using parameters."
/>
16
<
siteMapNode
url
="~/BasicReporting/ProgrammaticParams.aspx"
17
title
="Setting Parameter Values"
18
description
="Shows how to set parameter values
19
programmatically."
/>
20
</
siteMapNode
>
21
![](https://i-blog.csdnimg.cn/blog_migrate/635c746a47fe9fdafb7962bab037ffdb.gif)
22
<
siteMapNode
title
="Filtering Reports"
23
url
="~/Filtering/Default.aspx"
24
description
="Samples of Reports that Support Filtering"
>
25
<
siteMapNode
url
="~/Filtering/FilterByDropDownList.aspx"
26
title
="Filter by Drop-Down List"
27
description
="Filter results using a drop-down list."
/>
28
<
siteMapNode
url
="~/Filtering/MasterDetailsDetails.aspx"
29
title
="Master-Details-Details"
30
description
="Filter results two levels down."
/>
31
<
siteMapNode
url
="~/Filtering/DetailsBySelecting.aspx"
32
title
="Details of Selected Row"
33
description
="Show detail results for a selected item in a GridView."
/>
34
</
siteMapNode
>
35
![](https://i-blog.csdnimg.cn/blog_migrate/635c746a47fe9fdafb7962bab037ffdb.gif)
36
<
siteMapNode
title
="Customized Formatting"
37
url
="~/CustomFormatting/Default.aspx"
38
description
="Samples of Reports Whose Formats are Customized"
>
39
<
siteMapNode
url
="~/CustomFormatting/CustomColors.aspx"
40
title
="Format Colors"
41
description
="Format the grid's colors based
42
on the underlying data."
/>
43
<
siteMapNode
44
url
="~/CustomFormatting/GridViewTemplateField.aspx"
45
title
="Custom Content in a GridView"
46
description
="Shows using the TemplateField to
47
customize the contents of a field in a GridView."
/>
48
<
siteMapNode
49
url
="~/CustomFormatting/DetailsViewTemplateField.aspx"
50
title
="Custom Content in a DetailsView"
51
description
="Shows using the TemplateField to customize
52
the contents of a field in a DetailsView."
/>
53
<
siteMapNode
url
="~/CustomFormatting/FormView.aspx"
54
title
="Custom Content in a FormView"
55
description
="Illustrates using a FormView for a
56
highly customized view."
/>
57
<
siteMapNode
url
="~/CustomFormatting/SummaryDataInFooter.aspx"
58
title
="Summary Data in Footer"
59
description
="Display summary data in the grids footer."
/>
60
</
siteMapNode
>
61
![](https://i-blog.csdnimg.cn/blog_migrate/635c746a47fe9fdafb7962bab037ffdb.gif)
62
</
siteMapNode
>
63
![](https://i-blog.csdnimg.cn/blog_migrate/635c746a47fe9fdafb7962bab037ffdb.gif)
64
</
siteMap
>
站点地图定义了这个站点的导航结构,它是层次结构的以便描述站点中各种各样的区域。在Web.sitemap中的每个<siteMapNode>元素描述了一个站点结构中的一个区域。
图10:站点地图描述了一个层次的导航结构
Asp.net通过DotNET 框架中的
SiteMap类显示站点地图的结构。这个类有一个CurrentNode属性,它返回当前用户正在访问的节点的信息;RootNode属性返回站点地图的根节点信息(在我们的站点地图中是Home)。CurrentNode呵RootNode属性都返回
SiteMapNode实例,SiteMapNode包含ParentNode,ChildNodes,NextSibling,PreviousSibling等属性,这些属性允许站点地图的层次可以被遍历。
步骤4:利用站点地图显示菜单
在asp.net 2.0中我们可以像asp.net 1.x一样,有多种编程方式可以访问数据,还可以通过新的
数据源控件访问。
这里有多个内置的数据源控件,比如用来访问关系数据库数据的SqlDataSource控件,用来访问类所提供的数据的ObjectDataSoruce控件等等。你还可以创建你自己的
自定义数据源控件。
数据源控件作为你的aspx页面和底层数据的代理。为了显示数据源控件查询到的数据,我们要添加其他Web控件到页面上,并且将它和数据源控件绑定。要绑定一个Web控件到一个数据源控件,只需要简单的设置这个Web控件的DataSourceID属性值为数据源控件的ID属性值。
为了获取站点地图中的数据,asp.net提供了SiteMapDataSource控件,它允许我们绑定一个Web控件来显示我们的站点地图。TreeView和Menu这两个Web控件常常用来提供导航用户界面。要绑定站点地图中的数据到这两个控件,添加一个SiteMapDataSource控件到页面中,设置TreeView或者Menu控件的DataSourceID属性值为SiteMapDataSource控件的ID属性值就可以了。举个例子,我们可以用下面这些标记将Menu控件到母板页中:
1
<
div
id
="navigation"
>
2
<
asp:Menu
ID
="Menu1"
runat
="server"
3
DataSourceID
="SiteMapDataSource1"
>
4
</
asp:Menu
>
5
![](https://i-blog.csdnimg.cn/blog_migrate/635c746a47fe9fdafb7962bab037ffdb.gif)
6
<
asp:SiteMapDataSource
ID
="SiteMapDataSource1"
runat
="server"
/>
7
</
div
>
为了生成优化的HTML,我们可以绑定SiteMapDataSource控件到Repeater控件,如下:
1
<
div
id
="navigation"
>
2
<
ul
>
3
<
li
><
asp:HyperLink
runat
="server"
ID
="lnkHome"
4
NavigateUrl
="~/Default.aspx"
>
Home
</
asp:HyperLink
></
li
>
5
![](https://i-blog.csdnimg.cn/blog_migrate/635c746a47fe9fdafb7962bab037ffdb.gif)
6
<
asp:Repeater
runat
="server"
ID
="menu"
7
DataSourceID
="SiteMapDataSource1"
>
8
<
ItemTemplate
>
9
<
li
>
10
<
asp:HyperLink
runat
="server"
11
NavigateUrl
="<%# Eval("
Url") %
>
">
12
![](https://i-blog.csdnimg.cn/blog_migrate/09cf743c8c9460cbe8f30365138a3c50.gif)
<%
# Eval("Title")
%>
13
</
asp:HyperLink
>
14
</
li
>
15
</
ItemTemplate
>
16
</
asp:Repeater
>
17
</
ul
>
18
![](https://i-blog.csdnimg.cn/blog_migrate/635c746a47fe9fdafb7962bab037ffdb.gif)
19
<
asp:SiteMapDataSource
ID
="SiteMapDataSource1"
20
runat
="server"
ShowStartingNode
="false"
/>
21
</
div
>
SiteMapDataSource控件每次返回站点地图层次中的一级,从站点地图中的根节点开始(在我们的站点地图中是Home),然后是下一个级(Basic Reporting,Filtering Reports和Customized Formatting)等等。
当将SiteMapDataSource绑定到Repeater时,它遍历第一级并且用ItemTemplate显示第一级的每个SiteMapNode实例。我们可以使用Eval(属性名称)访问SiteMapNode的细节,这样我们就可以得到SiteMapNode的Url和Title属性给HyperLink控件。
下面显示的是上面使用Repeater控件例子生成的HTML标记:
1
<
li
>
2
<
a
href
="/Code/BasicReporting/Default.aspx"
>
Basic Reporting
</
a
>
3
</
li
>
4
![](https://i-blog.csdnimg.cn/blog_migrate/635c746a47fe9fdafb7962bab037ffdb.gif)
5
<
li
>
6
<
a
href
="/Code/Filtering/Default.aspx"
>
Filtering Reports
</
a
>
7
</
li
>
8
![](https://i-blog.csdnimg.cn/blog_migrate/635c746a47fe9fdafb7962bab037ffdb.gif)
9
<
li
>
10
<
a
href
="/Code/CustomFormatting/Default.aspx"
>
11
Customized Formatting
</
a
>
12
</
li
>
从上面可以看出,站点地图的第二级节点(Basic Reporting,Filtering Reports和Customized Formatting)被显示而不是第一个。
这是因为SiteMapDataSource控件的ShowStartingNode属性被设为false,导致SiteMapDataSource跳过了站点地图的根节点取而代之的是从站点地图的层次的第二级开始返回信息。
为了显示Basic Reporting,Filtering Reports和Customized Formatting的子SiteMapNode,我们可以向先前的Repeater的ItemTemplate里添加另外一个Repeater。第二个Repeater将绑定到SiteMapNode实例的子结点属性,如下:
1
<
asp:Repeater
runat
="server"
ID
="menu"
DataSourceID
="SiteMapDataSource1"
>
2
<
ItemTemplate
>
3
<
li
>
4
<
asp:HyperLink
runat
="server"
5
NavigateUrl
="<%# Eval("
Url") %
>
">
6
![](https://i-blog.csdnimg.cn/blog_migrate/09cf743c8c9460cbe8f30365138a3c50.gif)
<%
# Eval("Title")
%>
</
asp:HyperLink
>
7
![](https://i-blog.csdnimg.cn/blog_migrate/635c746a47fe9fdafb7962bab037ffdb.gif)
8
<
asp:Repeater
runat
="server"
9
DataSource
="<%# ((SiteMapNode)
10
Container.DataItem).ChildNodes %>"
>
11
<
HeaderTemplate
>
12
<
ul
>
13
</
HeaderTemplate
>
14
![](https://i-blog.csdnimg.cn/blog_migrate/635c746a47fe9fdafb7962bab037ffdb.gif)
15
<
ItemTemplate
>
16
<
li
>
17
<
asp:HyperLink
runat
="server"
18
NavigateUrl
="<%# Eval("
Url") %
>
">
19
![](https://i-blog.csdnimg.cn/blog_migrate/09cf743c8c9460cbe8f30365138a3c50.gif)
<%
# Eval("Title")
%>
</
asp:HyperLink
>
20
</
li
>
21
</
ItemTemplate
>
22
![](https://i-blog.csdnimg.cn/blog_migrate/635c746a47fe9fdafb7962bab037ffdb.gif)
23
<
FooterTemplate
>
24
</
ul
>
25
</
FooterTemplate
>
26
</
asp:Repeater
>
27
</
li
>
28
</
ItemTemplate
>
29
</
asp:Repeater
>
这两个Repeater生成的HTML标记(为了节省篇幅一些标记被移除了):
1
<
li
>
2
<
a
href
="/Code/BasicReporting/Default.aspx"
>
Basic Reporting
</
a
>
3
<
ul
>
4
<
li
>
5
<
a
href
="/Code/BasicReporting/SimpleDisplay.aspx"
>
6
Simple Display
</
a
>
7
</
li
>
8
<
li
>
9
<
a
href
="/Code/BasicReporting/DeclarativeParams.aspx"
>
10
Declarative Parameters
</
a
>
11
</
li
>
12
<
li
>
13
<
a
href
="/Code/BasicReporting/ProgrammaticParams.aspx"
>
14
Setting Parameter Values
</
a
>
15
</
li
>
16
</
ul
>
17
</
li
>
18
![](https://i-blog.csdnimg.cn/blog_migrate/635c746a47fe9fdafb7962bab037ffdb.gif)
19
<
li
>
20
<
a
href
="/Code/Filtering/Default.aspx"
>
Filtering Reports
</
a
>
21
![](https://i-blog.csdnimg.cn/blog_migrate/7b8419d166351d364d9f121b08f1aa8a.gif)
22
</
li
>
23
![](https://i-blog.csdnimg.cn/blog_migrate/635c746a47fe9fdafb7962bab037ffdb.gif)
24
<
li
>
25
<
a
href
="/Code/CustomFormatting/Default.aspx"
>
26
Customized Formatting
</
a
>
27
![](https://i-blog.csdnimg.cn/blog_migrate/7b8419d166351d364d9f121b08f1aa8a.gif)
28
</
li
>
使用的CSS风格选择自
Rachel Andrew的书:
The CSS Anthology: 101 Essential Tips, Tricks, & Hacks,<ul>和<li>元素的风格将显示如下:
图11:用两个Repeater和一些CSS显示的菜单
这个菜单在母板页中定义的,绑定了在Web.sitemap中定义的站点地图,这意味着所有站点地图的修改会立即反应到所有使用了Site.master母板页的页面。
关掉视图状态
所有的asp.net控件可以随意的保持它们的状态到
View State(译注:当原文中采用的是开头字母大写的ViewState将不翻译)中,最终生成HTML时它被系列化并保存在一个隐藏的表单域中。控件用ViewState来记忆它们在页面返回时被程序改变的状态,比如Web控件绑定的数据。如果视图状态允许信息可以在页面返回时保持,它会增大发送到客户端HTML代码的尺寸,如果在没有确切的监控下会使页面膨胀得很厉害。数据显示控件-尤其是GridView控件-会显著地增加大量的额外的标记到页面中。当然,这些增长可能对宽带用户毫无影响,但是视图状态会给拨号上网的用户增加几秒钟的延迟。
要观察视图状态的影响,在浏览器里打开这个页面然后查看页面的源代码(对于Internet Explorer,点击”查看”菜单并且选择源代码选项)。你还可以打开
页面跟踪选项以观察这个页面上每个控件的视图状态。视图状态的信息被系列化并放在位于跟随在<form>标签后面的<div>元素里的名为_VIEWSTATE的隐藏表单域中。
视图状态只在页面上使用了Form时才会被保持;如果你的aspx页面没有包含
<form runat=”server”>的声明,那么最后产生的HTML标记中将不含有VIEWSTATE隐藏表单域。
母板页产生的VIEWSTATE隐藏表单域大概有1800个字节。这些额外的数据主要是SiteMapDataSource控件为Repeater控件提供的数据内容产生的。也许1800字节左右看起来还不算很多,但是使用了GridView并且使用了很多字段和记录的视图状态很容易就膨胀10倍或更多。
可以将EnableViewState属性设为false在页面级或者控件级关闭视图状态,从而可以减少产生的标记的大小。Web控件利用视图状态在页面返回时保持要绑定到数据显示控件的数据,当关闭了数据显示控件的视图状态后,在每次页面返回时都必须重新绑定数据到控件。在asp.net 1.x的时候这个职责落到开发者身上;在asp.net 2.0里,页面返回时,数据显示控件会在必要的时候重新绑定数据。
设置Repeater控件的EnableViewState为false可以减少页面的视图状态。可以通过属性窗口设置或者在代码视图里手动修改。通过这些改变,Repeater标记将会像这样:
1
<
asp:Repeater
runat
="server"
ID
="menu"
DataSourceID
="SiteMapDataSource1"
EnableViewState
="False"
>
2
<
ItemTemplate
>
3
<
i
>
ItemTemplate contents omitted for brevity
</
i
>
![](https://i-blog.csdnimg.cn/blog_migrate/7b8419d166351d364d9f121b08f1aa8a.gif)
4
</
ItemTemplate
>
5
</
asp:Repeater
>
经过这些变化,页面产生的视图状态减少到52个字节,减少了97%的视图状态数据!在这个指南系列里我会关闭所有数据控件的视图状态以减少产生标记的大小。在大多数例子里会在没有提示的情况下将EnableViewState属性设为false。
仅有当数据Web控件必须打开它的视图状态才能提供期望的功能的情况下我们才讨论。
步骤5:添加breadcrumb导航
为完成母板页,让我们给每个页面添加一个breadcrumb导航UI元素。breadcrum导航会快速的显示用户当前在站点中的位置。添加一个breadcrumb导航在asp.net 2.0中是简单的-只要添加一个SiteMapPath控件到页面上就可以了;不需要更多的代码。
在我们的站点中,添加这个控件到头部的<div>标签中:
1
<
span
class
="breadcrumb"
>
2
<
asp:SiteMapPath
ID
="SiteMapPath1"
runat
="server"
>
3
</
asp:SiteMapPath
>
4
</
span
>
breadcrum导航控件显示了用户当前访问的页面以及它的父级节点,直至到根节点(在我们的站点地图中是Home)。
![](https://i-blog.csdnimg.cn/blog_migrate/104698a728e6d6d47b476367407f6266.gif)
图12:利用位置导航控件显示在站点地图层次中的当前页面及其父页面
步骤6:给每个部分添加默认页面
在我们的站点中这个课程被分成不同的分类-Basic Reporting,Filtering,Custom Formatting等等-每个分类有一个文件夹并且有对应课程的aspx页面。并且,每个文件夹里包含一个Default.aspx页面。在这个默认页面中,将显示这个部分的所有课程。比如,我们可以通过BasicReporting文件夹里的Default.aspx页面连接到SimpleDisplay.aspx,DeclarativeParams.aspx和ProgrammaticParams.aspx。这里,我们可以再次使用SiteMap类和一个数据显示控件显示定义在Web.sitemap文件内的站点地图的信息。
让我们再次使用Repeater显示一个无序列表,不过这次我们会显示指南的标题和描述。我们需要在每个Default.aspx页面重复这些标记和代码,我们可以将这个UI逻辑封装成一个User Control。在站点中添加一个名为UserControls的文件夹并添加一个名为SectionLevelTutorialListing.ascx的Web用户控件,它包含一下标记:
![](https://i-blog.csdnimg.cn/blog_migrate/40a49002cb6ff07c3bf9c4227eef6dde.gif)
图13:向UserControls文件夹里添加新Web用户控件
SectionLevelTutorialListing.ascx
1
<%
@ Control Language="C#" AutoEventWireup="true" CodeFile="SectionLevelTutorialListing.ascx.cs" Inherits="UserControls_SectionLevelTutorialListing"
%>
2
<
asp:Repeater
ID
="TutorialList"
runat
="server"
EnableViewState
="False"
>
3
<
HeaderTemplate
><
ul
></
HeaderTemplate
>
4
<
ItemTemplate
>
5
<
li
><
asp:HyperLink
runat
="server"
6
![](https://i-blog.csdnimg.cn/blog_migrate/09cf743c8c9460cbe8f30365138a3c50.gif)
NavigateUrl
="<%# Eval("
Url") %
>
" Text="
<%
# Eval("Title")
7
%>
">
</
asp:HyperLink
>
8
![](https://i-blog.csdnimg.cn/blog_migrate/09cf743c8c9460cbe8f30365138a3c50.gif)
-
<%
# Eval("Description")
%>
</
li
>
9
</
ItemTemplate
>
10
<
FooterTemplate
></
ul
></
FooterTemplate
>
11
</
asp:Repeater
>
SectionLevelTutorialListing.ascx.cs
1
using
System;
2
using
System.Data;
3
using
System.Configuration;
4
using
System.Collections;
5
using
System.Web;
6
using
System.Web.Security;
7
using
System.Web.UI;
8
using
System.Web.UI.WebControls;
9
using
System.Web.UI.WebControls.WebParts;
10
using
System.Web.UI.HtmlControls;
11
![](https://i-blog.csdnimg.cn/blog_migrate/635c746a47fe9fdafb7962bab037ffdb.gif)
12
public
partial
class
UserControls_SectionLevelTutorialListing : System.Web.UI.UserControl
13
![](https://i-blog.csdnimg.cn/blog_migrate/09cf743c8c9460cbe8f30365138a3c50.gif)
{
14
protected void Page_Load(object sender, EventArgs e)
15![](https://i-blog.csdnimg.cn/blog_migrate/c7fa726eb5c6ae5a58bf5b06254fd677.gif)
{
16
// If SiteMap.CurrentNode is not null,
17
// bind CurrentNode ChildNodes to the GridView
18
if (SiteMap.CurrentNode != null)
19![](https://i-blog.csdnimg.cn/blog_migrate/c7fa726eb5c6ae5a58bf5b06254fd677.gif)
{
20
TutorialList.DataSource = SiteMap.CurrentNode.ChildNodes;
21
TutorialList.DataBind();
22
}
23
}
24
}
在前面的Repeater例子中我将SiteMap的数据绑定到Repeater上;当然,这个SectionLevelTutorialListing用户控件也将使用这种方法。在Page_Load事件里,有一个检测程序以确保这是否是第一次访问该页面(不是返回)并且这个页面的URL要映射到站点地图中的一个节点。如果页面使用了这个用户控件,那么就没有对应的
<siteMapNode>,SiteMap.CurrentNode会返回null并且将没有数据绑定到Repeater控件。假设我们有一个CurrentNode,我可以将它的ChildNodes集合绑定到这个Repeater。每个部分的Default.aspx页面是这个部分内教程的父节点,这些代码会展示每个部分内教程的连接和描述,下面是屏幕截图:
一旦这个Repeater创建好后,在设计视图里打开每个文件夹的Default.aspx页面,将这个用户控件拖到你要显示的地方。
![](https://i-blog.csdnimg.cn/blog_migrate/737599f6f9acb208fb0dc2e12143671a.gif)
图14:用户控件已经添加到Default.aspx页面上![](https://i-blog.csdnimg.cn/blog_migrate/173579c0ce1fbcd243a34c069bcc8be5.gif)
图15:Basic Reporting指南的列表
总结
完成站点地图和母板页后,现在我们的教程站点拥有统一的页面布局和导航体系。尽管我们的站点有很多页面,但是我们可以集中的更新站点页面布局和站点导航信息。明确一点,页面布局信息在母板页Site.master中定义,站点地图在Web.sitemap中定义。我们不需要写任何代码就完成了站点页面布局和导航机制,Visual Studio提供了所见即所得的设计时支持。
完成了数据访问层和业务逻辑层并且定义了一个统一的页面布局和站点导航系统,下一步我们将探索通用报表模式。在接下来的三个指南里我们将会看到基本报表任务-用GridView,DetailsView和FormView控件显示从业务逻辑层获取的数据。