作者:Fritz Onion
相关技术:模板、ASP.NET,Master Pages
难度:★★★☆☆
读者类型:ASP.NET开发人员
[导读]本文首先介绍了在传统的Web开发下“模板”的实现,包括了ASP和ASP.NET 1.x的实现,然后介绍了ASP.NET 2.0下模板的实现方式——Master Pages,最后阐述了母版页的实现原理。
多年以来,Web开发人员一直在努力通过多种技术来“模板化”他们的站点,但没有任何一种经证明技术,能够在整个站点中维护标准化外观的真正通用,且可重用的方法。母版页作为在ASP.NET 2.0中问世的最令人期待的功能之一,最终提供了一种在应用程序中基于模板设计页面公认、一流的方法。
模板设计
在Web站点设计中为所有页面定义一种标准的外观是令人赏心悦目的。这可能包括公用页眉、页脚和菜单,他能够在整个站点中提供一组核心功能和外观。对于通过如ASP或ASP.NET这样的技术生成的动态站点而言,如果将所有页面上的这些公用功能整合为某种类型的页面模板将十分有用,这可以使各个页面只包含它自己的独特内容,并且提供一个在外观和行为方面进行站点范围更改的中心位置。有关可以从某种类型的页面模板技术中受益的站点的简单而具体的示例,请参见图1。
图1 简单的模板化站点
该特定页面在顶部有一个页眉,在底部有一个页脚,在左侧有一个导航栏,并且有一个填满了其余空间的用于页面特有内容的区域。理想情况下,页眉、页脚和导航栏应该只需要定义一次,并且以某种方式应用到该站点中的所有页面。
这正好是ASP.NET 2.0中的母版页能够简单而巧妙地解决的问题。通过定义一个母版页,并且随后根据它来创建许多内容页,您可以非常容易地用单个模板(母版页)所驱动的通用外观来创建站点。但是,在我探讨母版页的细节之前,了解一下目前开发人员如何在ASP和ASP.NET 1.x中生成模板化站点将是有用的。
在传统ASP中生成模板
利用ASP 3.0生成的许多站点都使用了模板的概念——一般是使用服务器端include (SSI)指令。SSI指令提供了在ASP页面内特定位置插入文件内容的能力。一种通用技术是使用SSI指令来引入常用的元素,如页脚、页眉和导航栏。图2演示了这种做法。
图2 ASP中的服务器端包含指令
尽管该技术在正确方向上迈出了一步,但它仍然意味着每个页面除了生成所有特定于页面的内容以外,还必须生成周围的布局元素和总体结构。许多站点采用的另一种更加具有模板特征的方法是:指定两个通用的包含文件(如pageStart.asp和pageEnd.asp),并且使这两个文件不仅包含通用的导航栏、页眉和页脚,还包含周围的布局元素和总体页面结构。留待各个页面添加的所有元素是它特有的内容。
该方法在整个站点的集中式模板中提供了许多开发人员需要的控制;但它也有几个缺点。首先,不支持用于编辑含有SSI指令的文件的设计器,因此设计人员必须实现合并的呈现效果以查看完整的页面外观,只是稍后需要将其重新分开。其次,包含机制过分简单,因为它只是在ASP分析程序进行服务器端计算之前将一个文件的内容插入到另一个文件中。这意味着在匹配HTML元素结束标记以及正确选择包含位置以避免错误呈现时,很容易出错。同时,很难自定义被包含文件的某些部分。在可以从MSDN Magazine Web站点下载的asp“technique2”示例中,您立即就可以注意到的一个缺陷是每个页面的标题都被有意保留为空。发生这种情况的原因在于:标题仅在顶级page_start包含文件中指定一次,并且没有什么巧妙的方法来让包含模板的页面指定应该在标题元素中出现的内容。最后,使用包含文件需要系统开销,因为它们增加了ASP脚本引擎缓存的总大小。
基于上述使用服务器端包含指令的简单方法,真正通用的模板化机制在理想情况下应该具有下列五个功能:
具有某种功能,使开发人员能够创建可以独立定义、并且可以在多个页面中重用的页面部分 能够为页面定义可供其他页面用作模板的“外壳” 页面能够改变继承的模板页面内部各种元素,例如更改标题元素 能够在页面内部以声明方式指定备用页面模板 能够使用与页面关联的模板实际查看该页面的呈现版本
将服务器端包含文件用于ASP 3.0可以满足上述第一个条件,并且可以在某种程度上满足第二个条件,但无法提供最后三个功能。
在ASP.NET 1.x中生成模板化站点
ASP.NET引入了一种全新的、基于对象的编程模型,从而有可能提供一种更新颖、更优雅的模板化机制。ASP.NET通过要呈现到客户端元素的完整层次化对象模型,向您提供了每个页面的服务器端表示形式,而不必依赖于ASP 3.0中比较低级的SSI机制。最恰当地反映SSI文件功能的功能是用户控件。用户控件提供了一种声明性方式,以便创建可以将其内容和行为插入其他页面任意位置的自定义控件,因此使用这些控件作为定义页面的可重用部分的通用方法是非常简单的,如图3所示。
图3 带有用户控件的可重用页面部分
除了充当简单内容占位符以外,用户控件还是能够公开属性、方法和事件功能的完整控件。与服务器端包含指令不同,您可以赋予用户控件能够影响其在页面上呈现的功能。例如,您可以在名为ShowBreadcrumbs的页眉控件上创建一个属性,以便能够根据需要选择性地打开或关闭面包屑功能。
但是,用户控件重用模型有一些局限和缺点。首先,许多开发人员发现,必须在将使用用户控件的每个页面中针对每个用户控件添加一条Register指令,因此非常麻烦。具有讽刺意味的是,许多ASP.NET开发人员已经转而使用SSI指令将Register指令集合导入他们的所有页面中,以便只用一条SSI指令替换多个代码行。然而,更为重要的是,没有任何一种内置方式可以使用用户控件来满足我在前面列出的有关开发模板化机制的五个条件中的任何一个(第一个条件除外,因为您可以创建可重用的页面部分)。
ASP.NET确实提供了一种更为丰富的对象模型,并且许多人已经建立了他们自己的模板化机制,以克服ASP.NET 1.x中缺少原生模板化机制的缺点。借助于一点儿创造性和一些聪明的编码,您可以生成通用的模板化机制。大多数实现都依赖于以下事实:您有机会在Page类为您自动构建的控件层次结构被发送回客户端之前操纵该控件层次结构。例如,一种技术是创建一个通用的、派生于Page的基类(该基类能够提取所生成的页面层次结构中的控件),动态加载充当页面模板的用户控件,然后将提取到的控件插入用户控件层次结构中的已知位置。该技术的完整实现包含在本文的代码下载资料中。
使用与此类似的技术,可以非常接近于达到真正有用的模板化机制所需要的全部五个条件。具有用于定义可重用页面部分的用户控件。具有一种页面模板机制,通过该机制,可以定义单个模板并将其应用于系统中任意数量的页面。使用该技术可以定义模板中的“可替换”元素,并且可以为每个页面指定备用模板。所缺少的一个功能就是设计器集成—如果没有Visual Studio .NET在识别这一模板化技术方面提供帮助,则要实现该功能确实是不可能的。
与此类似的自定义模板技术的另一个缺点是:没有一种完成这一工作的受到认可的方法,并且.NET Framework中没有支持这一技术的内置组件。这意味着每个站点所使用的模板化机制都可能完全不同。
尽管ASP.NET为您提供了极其灵活且更为强大的编程模型,但它仍然不具有用于模板化站点的内置机制。特别是,尽管某些开发人员已经通过巧妙地使用控件层次结构替换和用户控件生成了模板化机制,但他们仍需要完成额外的配置步骤,并且更为重要的是,他们不具有设计器支持。这时候,让我们走入ASP.NET 2.0中的母版页
母版页
ASP.NET 2.0中母版页的问世代表着Microsoft提供了第一个能够满足我在前面概述的全部条件的模板化机制。他们提供了站点级别的页面模板、一种用于进行细粒度内容替换的机制、对页面应该使用哪个模板的编程控制和声明性控制,或许最引人注目的是,他们提供了集成的设计器支持。从技术角度来说,母版页的实现方式与我所描述的ASP.NET 1.1中自定义模板机制的实现方式非常类似,但增加了来自Visual Studio 2005的设计器支持,并且它现在是受到认可和支持的通过可视化继承来构建模板化站点的方式,这一切使您最终