5.1 母版页概述
ASP.NET 2.0提供的母版页功能,为提高工作效率,降低开发和维护强度,提供了有力支持。本节首先对母版页和内容页的概念,以及应用过程进行简单说明,然后,详细介绍母版页运行机制,以及母版页和内容页事件顺序,最后对母版页的优点做出总结。
5.1.1 母版页基础知识
在ASP.NET 2.0中,可以将Web应用程序中的公用元素,例如,网站标志、广告条、导航条、版权声明等内容整合到母版页中。在这之前,开发人员更愿意将这一类似实现称为页面模板。实际上,可以将页面模板和母版页视为同一功能。换句话说,在ASP.NET 2.0中,可以将母版页看作是页面模板,而且是一种具有多项高级功能的页面模板。
同页面模板一样,母版页能够为ASP.NET应用程序创建统一的用户界面和样式,这是母版页的核心功能。在实现网站一致性的过程中,必须包含两种文件:一种是母版页,另一种是内容页。母版页后缀名是.master,其封装页面中的公共元素。内容页实际是普通的.aspx文件,它包含除母版页之外的其他非公共内容。在运行过程中,ASP.NET引擎将两种页面内容合并执行,最后将结果发给客户端浏览器。下面重点介绍母版页和内容页的基本概念。
常见母版页代码结构如下所示。
|
常见母版页代码结构 |
|
<%@ Master Language="C#" CodeFile="MasterPage.master.cs" Inherits="MasterPage" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server" > <title>母版页</title> </head> <body> <form id="form1" runat="server"> <table> <tr> <td><asp:contentplaceholder id="Main" runat="server" /></td> <td><asp:contentplaceholder id="Footer" runat="server" /></td> </tr> </table> </form> </body> </html> |
将以上母版页代码与普通.aspx文件代码比较,可以发现:虽然二者存在一些相似之处,然而还是存在以下3点差异。一是母版页的扩展名是.master,所有以master为后缀的文件都是母版页,这一点与普通.aspx文件不同。客户端浏览器可以向服务器发出请求,要求访问.aspx文件,但是,如果请求的是母版页,则不能执行访问。客户端可以访问内容页,通过内容页对母版页的绑定,才能够间接访问母版页。二是普通.aspx文件的代码头声明是<%@ Page %>,而母版页文件的代码头声明与此不同,它必须声明为<%@ Master %>,即将Page改换为Master。除此之外,母版页与普通.aspx文件在代码结构方面基本没有差异。例如,二者都需要声明<html>、<body>、<form>以及其他Web元素等。三是母版页中可以包括一个或者多个ContentPlaceHolder控件,而在普通.aspx文件中是不包含该控件的。ContentPlaceHolder控件起到一个占位符的作用,能够在母版页中标识出某个区域,该区域将内容页中的特定代码代替。
以上介绍了母版页与.aspx文件结构之间的差异。内容页与母版页关系紧密,下面简单介绍一下内容页。
内容页主要包含页面中的非公共内容。虽然内容页的扩展名是aspx,但是,其代码结构与普通.aspx文件有着很大差异。常见内容页的代码结构如下所示。
|
常见内容页代码结构 |
|
<%@ Page Language="C#" MasterPageFile="MasterPage.master" Title="Unknow Title" CodeFile="ContentPage.aspx.cs" Inherits="ContentPage_aspx" %> <asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" Runat="server"> ...... </asp:Content> |
由以上代码可知,内容页的代码主要分为两个部分:代码头声明和Content控件。内容页的代码头声明与普通.aspx文件很相似,只是增加了属性MasterPageFile和Title设置。属性MasterPageFile用于设置该内容页所绑定的母版页的路径,属性Title用于设置页面title属性值。另外,在内容页中,还可以包括一个或者多个Content控件。页面中所有非公共内容都必须包含在Content控件中。每一个Content控件通过属性ContentPlaceHolderID与母版页中的ContentPlaceHolder控件相连接。通过以上设置,就可以实现母版页与内容页的绑定。
图5-1所示显示的是示例站点Study.Com的页面截图。图中所示的页头和页尾部分是整个网站中每一个页面都要使用的公共部分。二者之间的区域,则包含了非公共部分内容。

图5-1 网页截图
如果使用ASP.NET 1.x技术实现以上应用,那么需要将页头和页尾作为两个用户控件处理。在创建如图5-1所示的页面时,首先引入包含页头的用户控件,然后为页面添加图片以及静态文字,即非公共内容,最后再加入包含页尾的用户控件。以上这种处理方法的核心是,应用了ASP.NET 1.x技术提供的用户控件功能。显而易见,需要创建多个用户控件才可能实现相关应用。如果使用ASP.NET 2.0提供的母版页功能,可以避免这一不足。
ASP.NET 2.0提供的母版页功能,允许开发人员创建真正意义上的页面模板。整个应用过程,可总结为“两个包含,一个结合”。
“两个包含”是指将页面内容分为公共部分和非公共部分,并且二者被分别包含在两个文件中。公共部分被包含在母版页中,非公共部分被包含在内容页中。开发人员可以根据所定义的公共内容,使用母版页来封装静态文本、HTML元素以及ASP.NET服务器控件等多种Web元素。需要注意的是,即使公共内容处于页面中的不同位置,仍然可以使用母版页功能将其内容整合到一个母版页文件中。对于页面内容中的非公共部分,只需在母版页中使用一个或者多个ContentPlaceHolder控件来占位即可。ContentPlaceHolder控件主要用于在母版页中,作为代替非公共部分的占位符号出现,而具体内容则被放置在内容页中。内容页的创建相对简单,只需将非公共内容包含在不同的Content控件中即可。
“一个结合”是指通过控件应用以及属性设置等行为,将母版页和内容页有机结合。例如,母版页中ContentPlaceHolder控件的ID属性必须与内容页中Content控件的ContentPlaceHolderID属性绑定。
5.1.2 母版页运行机制
前文已经说明,单独的母版页是不能被用户所访问的。没有内容页支持,母版页仅仅是一个页面模板,没有更多的实用价值。同样道理,单独的内容页没有母版页支持,也不能够应用。由此可见,母版页与内容页关系密切,是不可分割的两个部分。只有同时正确创建和使用母版页以及内容页,才能发挥它们的强大功能。这一点,无论从代码结构,还是运行机制等方面都可以得到有力印证。
首先,从代码结构方面深入说明,以便读者了解母版页与内容页的运行机制。
由上文可知,母版页内容以页面公共部分为主,包括代码头、ContentPlaceHolder控件以及其他常见Web元素。内容页主要包含页面非公共部分,包括两个部分:代码头和Content控件。Content控件中包含着页面非公共内容。图5-2所示显示了母版页和内容页的控件对应关系。

图5-2 控件对应关系图
在控件应用方面,母版页和内容页有着严格对应关系。母版页中包含多少个ContentPlaceHolder控件,那么内容页中也必须设置与其相同数目的Content控件。图5-2说明的是母版页和内容页之间的控件对应关系。母版页中包含两个ContentPlaceHolder控件cph1和cph2。内容页则必须对应两个Content控件,并且Content控件的属性ContentPlaceHolderID的设置必须与cph1和cph2相对应。如果将母版页的ContentPlaceHolder控件看作是页面中的占位符,那么占位符所对应的具体内容就包含在内容页的Content控件中。二者的对应关系是通过设置Content控件中的ContentPlaceHolderID属性来完成的。
在实际应用中,为了给整个网站创建一致的风格和样式,一个母版页可能被多个内容页绑定。只有正确处理母版页与内容页之间的控件对应关系,才能够准确、高效地创建Web应用程序。
下面重点对母版页的运行过程进行说明。
当客户端浏览器向服务器发出请求,要求浏览页面时,ASP.NET执行引擎将执行内容页和母版页的代码,并将最终结果发送给客户端浏览器。整个过程如图5-3所示。

图5-3 运行过程图
母版页和内容页的运行过程可以概括为以下5个步骤。
(1)用户通过键入内容页的URL来请求某页。
(2)获取内容页后,读取@ Page指令。如果该指令引用一个母版页,则也读取该母版页。如果是第一次请求这两个页,则两个页都要进行编译。
(3)母版页合并到内容页的控件树中。
(4)各个Content控件的内容合并到母版页中相应的ContentPlaceHolder控件中。
(5)呈现得到结果页。
整个过程具有很强的逻辑性,并且母版页和内容页配合的非常巧妙。从用户角度来看,合并后的母版页和内容页是一个完整的页面,并且其URL访问路径与内容页的路径相同。从开发人员角度来看,控件的巧妙应用和配合,是实现的关键。注意,在运行时,母版页成为了内容页的一部分。实际上,母版页与用户控件的作用方式大致相同,即作为内容页的一个子级,并作为该页中的一个容器。然而,当前母版页是所有呈现到浏览器中的服务器控件的容器。此时,回想过去使用的#include、用户控件等实现方法,则可以更加深入理解母版页功能。
5.1.3 母版页和内容页事件顺序
通常情况下,母版页和内容页中的事件顺序对于页面开发人员并不重要。但是,如果所创建的事件处理程序取决于某些事件的可用性,那么了解母版页和内容页中的事件顺序很有帮助。本节将对母版页和内容页的事件顺序进行简要说明,以便加深读者对于母版页和内容页的理解。
当访问结果页时,实际访问的是内容页和母版页。作为有着密切关系的两个页面,二者都要执行各自的初始化和加载等事件。具体过程如图5-4所示。

图5-4 母版页和内容页加载过程图
加载母版页和内容页共需要经过8个过程。这8个过程显示初始化和加载母版页及内容页是一个相互交叠的过程。基本过程是,初始化母版页和内容页控件树,然后,初始化母版页和内容页页面,接着,加载母版页和内容页,最后,加载母版页和内容页控件树。以上8个过程对应着11个具体事件。这些事件如下所示。
(1)母版页中控件Init事件;
(2)内容页中Content控件Init事件;
(3)母版页Init事件;
(4)内容页Init事件;
(5)内容页Load事件;
(6)母版页Load事件;
(7)内容页中Content控件Load事件;
(8)内容页PreRender事件;
(9)母版页PreRender事件;
(10)母版页控件PreRender事件。
(11)内容页中Content控件PreRender事件。
实际上,8个过程或者是11个事件都用于说明母版页和内容页中的具体事件顺序。内容页和母版页中会引发相同的事件。例如,两者都引发Init、Load和PreRender事件。引发事件的一般规律是,初始化Init事件从最里面的控件(母版页)向最外面的控件(Conetent控件及内容页)引发,所有其他事件则从最外面的控件向最里面的控件引发。需要牢记,母版页会合并到内容页中,并被视为内容页中的一个控件,这一点十分有用。
在创建应用程序中,必须注意以上事件顺序。例如,当在内容页中访问母版页的属性或者服务器控件时,如果按照过去的处理思路,可能会在内容页的Page_Load事件处理程序中加以实现。由前文可知,在母版页Load事件引发之前,内容页Load事件已经引发,那么过去的思路显然是不正确的。如何才能在内容页中访问母版页包含对象呢?可参考“5.5访问母版页控件和属性”一节中介绍的实现方法。
5.1.4 母版页的优点
使用母版页功能,可以为ASP.NET应用程序页面创建一个通用的外观。开发人员可以利用母版页功能创建一个单页布局,然后将其应用到多个内容页中。总结起来,母版页具有以下4个优点。
有利于站点修改和维护,降低开发人员的工作强度
由于公共内容被集中于母版页中,因此,只要修改母版页,就可以快速完成站点修改和维护任务,这在很大程度上提高了工作效率。
提供高效的内容整合能力
在母版页中,允许添加文字、控件等Web元素,同时,也可以为这些Web元素添加相应事件处理程序等。例如,只需要在母版页中添加一个服务器控件及其事件处理程序,那么站点内所有引用该母版页的网页,都将获得同样的应用效果。
有利于实现页面布局
母版页中的ContentPlaceHolder控件起到了占位的作用,这在很大程度上,有利于进行页面布局等工作。
提供一种便于利用的对象模型
由内容页和母版页组成的对象模型,能够为应用程序提供一种高效、易用的实现方式,并且这种对象模型的执行效率较以前的处理方式有了很大提高。







