发布日期: 4/1/2004 | 更新日期: 4/1/2004
Paul Wilson
ASP.NET 领域内的 Microsoft 最有价值专家
2003 年 10 月
适用于:
Microsoft ASP.NET Whidbey
Microsoft Visual Basic .NET
摘要:了解母版页特性,这是即将面世的 ASP.NET 版本中出现的一种灵活的页面模板系统,可提供卓越的可视化设计器支持,其代号为 "Whidbey"(根据即将面世的 Microsoft Visual Studio .NET 发布版的代号命名)。(10 页打印页)
下载 ASP.NET Pro 示例。
内容
注 相应的软件产品正式发行之前,本文档已经撰写完毕,因此我们不能保证本文档内所涉及的任何细节与最终交付的产品完全一致。文中的描述信息针对本文发表时的产品,仅供在规划时参考之用。如有更改,恕不另行通知。
大多数 Web 站点的所有页面共享一种布局,但是传统的 ASP 和 ASP.NET 都未曾包括对页面模板的自动支持。并且,虽然传统 ASP 中通常都使用包含文件,而 ASP.NET 中经常使用用户控件和基本 Page 类,但是这些方法都不是内置的,且不具备设计器支持。通过加入一种名为母版页的灵活页面模板系统,Microsoft ASP.NET Whidbey 改变了这种状况。这种模板系统应该足够简单以便设计人员使用,同时其功能又足够强大以供开发人员使用。
要学习这种新的母版页方法,最简单的途径就是考察一些示例,因此,本文的剩余部分将给你讲述若干示例,从最简单的情况开始,逐步扩展到更高级的情况。
第一个示例创建一个带有站点标题、左侧和右侧边栏、站点脚注和单个内容“区域”的母版,以此说明母版页的各种新概念和语法。首先,创建一个母版页作为公共布局。母版页与用户控件非常相似,不同之处在于它具有新的 master 文件扩展名以及一个 Master 指令。与大多数用户控件不同,母版页将包含通常位于每个独立页面中的顶级 HTML 标记,例如 <html>、 <head>、 <body> 和 <form>。只需用普通的 HTML 标记和服务器控件(在这种情况下,包括一个用于定义标题和脚注的表格,以及另一个用于定义左侧和右侧边栏的内嵌表格),创建公共布局(参阅图 1)即可。在实际页面中将被替换掉的内容区域随后由新的 ContentPlaceHolder 控件进行标记,并将在实际页面中匹配此处分配给该控件的 ID。此 ContentPlaceHolder 控件中所包含的任何内容就成为默认内容(仅当实际页面没有覆盖此默认内容的匹配内容区域时,才会呈现此默认内容)。
代码示例 1. 此 IntroLayout.master 文件是母版页,用于定义其他页面的公共布局。其中包括标题、左侧和右侧边栏,脚注和单个内容区域。
<%@ master language="VB" %>
<html>
<head>
<title>Master Pages</title>
<link rel="stylesheet" type="text/css"
href="StyleSheet.css" />
</head>
<body>
<form runat="server">
<table width="100%" cellpadding="0" cellspacing="0">
<tr class="head">
<td height="25">Site Header Goes Here</td></tr>
<tr><td>
<table width="100%" cellpadding="5" cellspacing="0">
<tr valign="top" height="400">
<td width="200" class="left">
Site Left SideBar Goes Here</td>
<td>
<h1 align="center">Master Pages</h1>
<asp:contentplaceholder id="Main" runat="Server">
This is Default Content -- Override on Page
</asp:contentplaceholder></td>
<td width="200" class="right">
Site Right SideBar Goes Here</td>
</tr>
</table>
</td></tr>
<tr class="foot">
<td height="25">Site Footer Goes Here</td></tr>
</table>
</form>
</body>
</html>
最后,为页面所特有的内容创建一个内容页面,其中 Page 指令中使用了新的 Master 属性以便链接母版页。除了一个可选的服务器脚本块之外,唯一允许使用的顶级元素就是新的 Content 控件,这种控件替换匹配的 ContentPlaceHolder 控件(在母版页中定义)。Content 控件具有一个 ContentPlaceHolderID 属性,这个属性必须与它要覆盖的 ContentPlaceHolder 控件原先被指定的 ID 相匹配。否则,该内容区域中页面所特有的内容包括普通 HTML 标记和服务器控件,包括页面或控件事件处理程序。请注意,通过包含一个带有 Master 属性的 <pages> 元素,我们可以在 web.config 文件中指定一个默认的母版,但在设计器中当前尚不支持这一功能。
代码示例 2. 此 IntroContent.aspx 文件为使用了 IntroLayout.master 母版页的内容页面。其中仅包含与该母版页中所定义的区域相对应的页面特有内容。
<%@ page language="VB" master="~/IntroLayout.master" %>
<script runat="server" language="vb">
Sub Submit_Click(ByVal sender As Object, _
ByVal e As System.EventArgs)
Result.Text = TestValue.Text
End Sub
</script>
<asp:content id="Main" runat="server"
contentplaceholderid="Main">
<div align="center">
<h3>Introduction to Master Pages</h3>
This is Page Content Overridden from Master<p />
<asp:textbox id="TestValue" runat="server" /><p />
<asp:button id="Submit" runat="server" text="Submit"
onclick="Submit_Click" /><p />
Result: <asp:literal id="Result" runat="server" />
</div>
</asp:content>
我们当然可以仅使用 SDK 完全在记事本中编写本示例,但当使用像 "Whidbey" 版本的 Microsoft Visual Studio .NET 这样的 IDE 时,母版页的价值才真正体现出来。图 1 显示了当使用此母版页时该内容页面的设计时视图,从而在设计时显示整个页面如何进行视觉呈现。其中显示了母版页本身,但该母版页是灰色的,因为它本身是不可编辑的,而内容页面则是完全可设计的,其周围带有全新的设计任务面板。与先前版本的 Visual Studio .NET 中所使用的所有页面模板方法不同,此处完全具有所有的 Microsoft IntelliSense,拖放或任何其他设计特性。
图 1. Whidbey 版本的 Visual Studio .NET 包括对母版页的设计时支持。母版页本身显示为灰色,而页面特定的内容是可编辑的。
多区域和 Header 标记接下来的示例说明了如何在母版页中使用多个内容区域,并演示了如何利用自定义的公共属性来扩展母版页。首先,将右侧边栏更改为一个 ContentPlaceHolder 控件,在这种情况下为 ID Right,这样在链接到此母版的每个页面中,该区域也可被替换为一个 Content 控件。这个新的区域将显示在 Visual Studio .NET 设计器中且是完全可编辑的,就如其他内容区域一样,并且如果在实际的页面中这个区域未被覆盖,则将呈现其默认内容。接下来,将 <title> 标记改为 ContentPlaceHolder 控件的一部分,这样就可以方便地在每个实际页面中设置标题、样式表链接和 metatag。请注意,此内容区域将不会显示在设计器中,因为它位于 head 而不是 body 中,但就像普通的 head 标记一样,可以很方便地在 HTML 源文件中进行编辑。接下来,创建一个名为 LeftSideBar 的公共字符串属性(或在这种情况下为简单起见,创建字段),并为左侧边栏创建一个 Literal 控件,将该控件的 Text 属性设置为此属性母版 PreRender 事件中的值。在此简单示例中,本来还可以创建另一个内容区域,但重要的是要知道,对于可能遇到的更复杂的情况来说,母版页中自定义的公共属性是易于创建和使用的。
代码示例 3. 此 MultipleRegions.master 文件具有多个区域,其中一个区域用于标题和其他 head 标记。其中还定义了一个自定义的公共属性,该属性可在每个实际页面中方便地进行设置。
<%@ master language="VB" %>
<script runat="server" language="vb">
Public LeftSideBar As String _
= "Master.LeftSideBar Goes Here"
Sub Page_PreRender(ByVal sender As Object, _
ByVal e As System.EventArgs)
Left.Text = LeftSideBar
End Sub
</script>
<html>
<head>
<asp:contentplaceholder id="Head" runat="server">
<title>Master Pages</title>
</asp:contentplaceholder>
<link rel="stylesheet" type="text/css"
href="StyleSheet.css" />
</head>
<body>
<form runat="server">
<table width="100%" cellpadding="0" cellspacing="0">
<tr class="head">
<td height="25">Site Header Goes Here</td></tr>
<tr><td>
<table width="100%" cellpadding="5" cellspacing="0">
<tr valign="top" height="400">
<td width="200" class="left">
<asp:literal id="Left" runat="Server">
Master.LeftSideBar Goes Here
</asp:literal></td>
<td>
<h1 align="center">Master Pages</h1>
<asp:contentplaceholder id="Main" runat="Server">
This is Default Content -- Override on Page
</asp:contentplaceholder></td>
<td width="200" class="right">
<asp:contentplaceholder id="Right" runat="Server">
Site Right SideBar Goes Here
</asp:contentplaceholder></td>
</tr>
</table>
</td></tr>
<tr class="foot">
<td height="25">Site Footer Goes Here</td></tr>
</table>
</form>
</body>
</html>
最后,更改内容页面,适当使用匹配的 Content 控件覆盖新的区域,或者如果想要保持每个区域所定义的默认内容,则可不作改动。记住,对于那些用于 HTML head 内容(包括标题、样式表链接和 metatag)的区域,必须手动地在 HTML 源文件中进行编辑,因为设计器中仅显示 body 区域。最后,尽管您可能需要首先保存或重新加载该母版,也要在 Load 事件中设置该母版页自定义公共属性的值(该值甚至在 IntelliSense 中也可使用)。
代码示例 4. 此 MultipleRegions.aspx 文件为使用了 MultipleRegions.master 母版页的内容页面。其中包括多个区域(包括 HTML head 中的一个区域)并使用一个自定义的属性。
<%@ page language="VB" master="~/MultipleRegions.master" %>
<script runat="server" language="vb">
Sub Page_Load(ByVal sender As Object, _
ByVal e As System.EventArgs)
Master.LeftSideBar = "Page-Specific Left SideBar"
End Sub
Sub Submit_Click(ByVal sender As Object, _
ByVal e As System.EventArgs)
Result.Text = TestValue.Text
End Sub
</script>
<asp:content id="Head" runat="server"
contentplaceholderid="Head">
<title>Master Pages: Multiple and Head Regions</title>
</asp:content>
<asp:content id="Main" runat="server"
contentplaceholderid="Main">
<div align="center">
<h3>Multiple and Head Regions</h3>
This is Page Content Overridden from Master<p />
<asp:textbox id="TestValue" runat="server" /><p />
<asp:button id="Submit" runat="server" text="Submit"
onclick="Submit_Click" /><p />
Result: <asp:literal id="Result" runat="server" />
</div>
</asp:content>
<asp:content id="Right" runat="server"
contentplaceholderid="Right">
Page-Specific Right SideBar
</asp:content>
嵌套和动态母版最后一个示例说明嵌套母版页的使用,并演示如何启用在运行时根据用户输入而确定的动态母版页。请注意,至少对本测试版本而言,设计器中不支持嵌套的母版,且并不正式推荐在此测试版本中实现动态母版所需的方法,因为在后续的版本中应该提供更好的正式支持的方法。首先,创建一个如前所述的仅带站点标题、站点脚注和单个子内容区域以及一个HTML head 区域的父级母版。
接下来,创建一个子级母版以替换该子区域,该子级母版的左侧边栏公开为一个自定义属性,右侧边栏公开为一个内容区域,中央区域公开为主内容区域。此外,还必须利用另一个内容区域覆盖并替换 HTML head 区域,以便对实际的页面公开该区域,至少对当前的测试版本而言必须如此(参见附带的代码下载)。
最后,更改内容页面以便链接到此子级母版,并如前所述覆盖内容区域。接下来,在页面指令中添加一个“设备筛选器”母版,在这种情况下,该母版将被命名为 "alternate"。然后,重写 TestDeviceFilter 方法并在此 alternate 母版的条件满足时返回 True。在这种情况下,当存在回发时就使用 alternate 母版,尽管必须使用 Form 集合来确定这种情况(因为在此页面生存周期的早期阶段,IsPostBack 尚不可用)。最后,在 Load 事件中设置该母版页的自定义公共属性值,尽管这将需要该母版的类型强制转换(因为母版现在是动态的)。此外还要注意,并不正式推荐这种方法,但这是在测试版本中实现动态母版的唯一途径,这一方法尚在讨论之中且很可能在最终发布之前进行更改以更好地支持动态母版。
代码示例 5. 此 NestedLayouts.aspx 文件为使用了嵌套 ChildLayout.master 母版页的内容页面。当页面是回发时,其中还动态地使用了 MultipleRegions.master。
<%@ page language="VB" master="~/ChildLayout.master"
alternate:master="~/MultipleRegions.master" %>
<script runat="server" language="vb">
Overrides Function TestDeviceFilter( _
ByVal deviceFilterName As String) As Boolean
If deviceFilterName.Equals("alternate") Then
If Request.Form.Count > 0 Then
Return True
End If
End If
Return MyBase.TestDeviceFilter(deviceFilterName)
End Function
Sub Page_Load(ByVal sender As Object, _
ByVal e As System.EventArgs)
If TypeOf Master Is ASP.ChildLayout_master Then
CType(Master, ASP.ChildLayout_master) _
.LeftSideBar = "Nested Layouts Left SideBar"
ElseIf TypeOf Master Is ASP.MultipleRegions_master Then
CType(Master, ASP.MultipleRegions_master) _
.LeftSideBar = "Multiple Regions Left SideBar"
End If
End Sub
Sub Submit_Click(ByVal sender As Object, _
ByVal e As System.EventArgs)
Result.Text = TestValue.Text
End Sub
</script>
<asp:content id="Head" runat="server"
contentplaceholderid="Head">
<title>Master Pages: Nested and Dynamic Masters</title>
</asp:content>
<asp:content id="Main" runat="server"
contentplaceholderid="Main">
<div align="center">
<h3>Nested and Dynamic Masters</h3>
This is Page Content Overridden from Master<p />
<asp:textbox id="TestValue" runat="server" /><p />
<asp:button id="Submit" runat="server" text="Submit"
onclick="Submit_Click" /><p />
Result: <asp:literal id="Result" runat="server" />
</div>
</asp:content>
<asp:content id="Right" runat="server"
contentplaceholderid="Right">
Page-Specific Right SideBar
</asp:content>
母版页的优点母版页系统易于供设计人员使用,因为它基于 ASP.NET 的熟悉的用户控件模型。尽管最终加入了近乎完整的可视化,却不需要编写任何代码。另一方面,母版页的功能强大,因为它们支持多区域、默认内容、嵌套模板、和设备筛选器(用于浏览器依赖性)。母版页也是完全编译的,从而具有最佳性能,同时提供一种强类型编程模型(其中包括母版属性的设计时 IntelliSense),尽管在最后发行之前可能会作一些折衷,以便更好地支持动态母版。
当然了,ASP.NET Whidbey 的正式发行版本还没有发布。然而,有几个版本的母版页能够在版本 1.0 和 1.1 下运行,尽管这些版本理所当然地尚未内置集成到 Visual Studio 的设计器中。有关其他信息,请参阅 ASP.NET 联盟 Web 站点上 Paul Wilson's ASP.NET Corner 中的以下两篇文章:MasterPages:Introduction 是关于 Microsoft 的最初示例的,而 MasterPages:Improved Version 提供了一种自定义的版本,不会破坏现有的设计器。本文所提供的代码下载也包含与这篇文章中几乎完全相同的示例,其中在版本 1.0 和 1.1 中使用了我自定义版本的母版页。
关于作者
Paul Wilson 是亚特兰大的一名软件架构师,他最近加入了 PRG-Schultz 的开发小组。他的 WilsonWebForm 控件允许在 ASP.NET 中使用多窗体和非回发窗体。他是 ASP.NET 领域内的 Microsoft 最有价值专家、Microsoft 的 ASP.NET 论坛的管理员,同时还是 Microsoft 认证解决方案开发人员、Microsoft 认证应用程序开发人员、Microsoft 认证数据库管理员和 Microsoft 认证系统工程师。如果希望与他联系,可访问他的 Web 站点,www.WilsonDotNet.com,或者发送电子邮件至 Paul@WilsonDotNet.com。
本文最初发表于 asp.netPRO Magazine 2003 年 12 月刊。出版商授权重印。