最近用Asp.net2.0作了一个企业门户的项目,在使用其新特性母版页(MasterPage)的时候发现有些地方不尽如人意,因此自己开发了一套新的母版控件(Keyss.MasterControls),以下是两者的优缺点对比供大家参考:

MasterPage:
优点:
当单继承时有设计时支持与asp.net2.0结合紧密。
缺点:
a.仅为页面设计,不支持控件
b.支持多重继承但,不支持隔代替换
c.不支持ContentPlaceHolder嵌套

Keyss.MasterControls:
优点:
a.同时为页面和控件设计
b.支持多重继承,支持页面母版的隔代替换
c.同时支持页面母版和控件母版的ContentPlaceHolder嵌套(替换子ContentPlaceHolder时规则为广度优先)
缺点:
无设计时支持

下面针对上面提到的mastercontrol的优点作些解释:
a.Keyss.MasterControls同时支持页面和控件母版。
支持页面母板和MasterPage是一样的,如下面两张图一个是首页,一个是内容页,他们的页头,主菜单,页尾以及网站导航区是完全一样的。
 
因此我为他们共同定义了一个页面模板。如下所示:
<%@ Control Language="C#" AutoEventWireup="true" CodeFile="PageMaster.ascx.cs" Inherits="App_Master_PageMaster" %>
<%@ Register Assembly="Keyss.WebControls" Namespace="Keyss.WebControls" TagPrefix="KSS" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
    
<title></title>
    
<meta name="description" id="searchDescription" runat="server" />
    
<meta name="keywords" id="searchKeywords" runat="server" />
</head>
<body style="text-align:center;">
    
<form id="form1" runat="server">
        
<table cellspacing="0" cellpadding="0" border="0" style="width: 768px">
            
<tr>
                
<td>
                    
<KSS:FlashMovie ID="fmBanner" runat="server" Width="768" Height="120" FlashUrl="~/App_Master/Images/Banner.swf" />
                
</td>
            
</tr>
            
<tr>
                
<td class="menuBar">
                    
<asp:Repeater ID="MenuMain" runat="server" DataSourceID="xdsMainMenu">
                        
<HeaderTemplate>
                            
<table border="0" cellspacing="0" cellpadding="0" style="height: 28px">
                                
<tr>
                        
</HeaderTemplate>
                        
<ItemTemplate>
                            
<td>
                                
<asp:HyperLink ID="hlMainMenu" CssClass="MenuMain" NavigateUrl='<%#Eval("url")%>'
                                    runat="server">
<%#Eval("title")%></asp:HyperLink></td>
                            
<td>
                                
<img alt="" src='<%= this.Page.ResolveClientUrl("~/App_Master/Images/divider.gif") %>' />
                            
</td>
                        
</ItemTemplate>
                        
<FooterTemplate>
                            
</tr></table>
                        
</FooterTemplate>
                    
</asp:Repeater>
                
</td>
            
</tr>
            
<tr>
                
<td>
                    
<table cellspacing="0" cellpadding="0" border="0" style="width: 100%; background-color: White;">
                        
<tr>
                            
<td align="center" class="navigationPanel" rowspan="3" style="width: 215px" valign="top">
                                
<KSS:ContentPlaceHolder ID="cphNavPanel" runat="server" IsGlobal="true">
                                
</KSS:ContentPlaceHolder>
                            
</td>
                            
<td class="siteMapBar">
                                当前位置:
<asp:SiteMapPath ID="smp" runat="server">
                                
</asp:SiteMapPath>
                            
</td>
                        
</tr>
                        
<tr>
                            
<td valign="top" style="border-bottom: solid 1 White;"><KSS:ContentPlaceHolder ID="cphImage" runat="server" IsGlobal="true">
                                
</KSS:ContentPlaceHolder></td>
                        
</tr>
                        
<tr>
                            
<td valign="top" style="border-bottom: solid 1 White; height:200px">
                                
<KSS:ContentPlaceHolder ID="cphMain" runat="server" IsGlobal="true">
                                    
<table cellspacing="0" cellpadding="0" border="0" style="width: 100%; ">
                                        
<tr>
                                            
<td valign="top" style="border-bottom: solid 1 White; padding:5px;">
                                                
<KSS:ContentPlaceHolder ID="cphContent" runat="server" IsGlobal="true">
                                                
</KSS:ContentPlaceHolder>
                                            
</td>
                                            
<td class="helpPanel" valign="top" style="border-bottom: solid 1 White;">
                                                
<KSS:ContentPlaceHolder ID="cphHelp" runat="server" IsGlobal="true">
                                                
</KSS:ContentPlaceHolder>
                                            
</td>
                                        
</tr>
                                    
</table>
                                
</KSS:ContentPlaceHolder>
                            
</td>
                        
</tr>
                        
<tr>
                            
<td class="navigationPanel" valign="bottom" align="center" style="width: 215px;">
                            
</td>
                            
<td class="copyrightBar">
                                
<%= OnlineContact %>
                                
<br />
                                
<%= Copyright %>
                            
</td>
                        
</tr>
                    
</table>
                
</td>
            
</tr>
        
</table>
        
<asp:XmlDataSource ID="xdsMainMenu" runat="server" DataFile="~/App_Master/MainMenu.xml"
            XPath
="/*/*/*"></asp:XmlDataSource>
        
<asp:XmlDataSource ID="xdsFooterMenu" runat="server" DataFile="~/App_Master/FooterMenu.xml"
            XPath
="/*/*/*"></asp:XmlDataSource>
    
</form>
</body>
</html>

然后针对不同的页面我只要用不同的东西替换掉页面中的内容即可。
而在上两张图中右下角(主页的常见问题解答,下载中心及销售网络和内容页的联系管理员)我也用了mastercontrol来生成深红条的panel模版。如下所示:
<%@ Register Assembly="Keyss.WebControls" Namespace="Keyss.WebControls" TagPrefix="KSS" %>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    
<tr>
        
<td style="font-size: 1pt; background-color: #710D0D; height: 5px;">
        
</td>
    
</tr>
    
<tr>
        
<td><KSS:ContentPlaceHolder ID="Main" runat="server">
                
<div style="padding: 5px;">
                    
<span class="SubTitleRed">
                        
<KSS:ContentPlaceHolder ID="Title" runat="server">
                        
</KSS:ContentPlaceHolder>
                    
</span>
                    
<br />
                    
<KSS:ContentPlaceHolder ID="Content" runat="server">
                    
</KSS:ContentPlaceHolder>
                
</div>
            
</KSS:ContentPlaceHolder></td>
    
</tr>
</table>
然后用不同的内容将其替换到主页和内容页中,以达到图上效果。当然这个在这用这个模板显得有些重量级了些,毕竟html代码在此列中相对简单
但针对复杂的html代码,其同样适用。

b.支持多重继承,支持页面的隔代替换
如上面提到的页面母版中,我重写了asp.net2.0中的ContentPlaceHolder使其支持IsGlobal属性,该属性用来表明,此占位符在一个请求上下文中是全局的并且ID也是唯一的,这也就意味着无论我从母板继承了多少代,都可以定位并替换此占位符。举个例子,我所有的栏目页都是从下面这个模板继承来的
<%@ Control Language="C#" AutoEventWireup="true" CodeFile="PageArticleCategory.ascx.cs" Inherits="App_Master_PageArticleCategory" %>
<%@ Register Assembly="Keyss.WebControls" Namespace="Keyss.WebControls" TagPrefix="KSS" %>
<KSS:ContentContainer ID="cc1" MasterControlFile="~/App_Master/PageMaster.ascx" runat="server">
    
<KSS:Content ID="c2" ContentPlaceHolderID="cphImage" runat="server"><KSS:Image ID="imgMainImage" BorderWidth="0" runat="server" Width="552" /></KSS:Content>
    
<KSS:Content ID="c3" ContentPlaceHolderID="cphContent" runat="server">
        
<span class="SubTitleRed"><%= this.ArticleCategory.Title%></span><br />
        
<%=this.ArticleCategory.Content %>
    
</KSS:Content>
</KSS:ContentContainer>
而这个模板又是从上面的PageMaster继承过来的,这个模板并没有作什么工作,只是每个栏目页都显示一个图片,显示一段文章内容,这样的话,我就可以在后台在统一的位置维护这些图片如下图所示:




但真正的栏目页不可能是千篇一律的,比方说,在登录的时候右下角要显示登录帮助,而在关于我们的右下角要显示联系管理员,这个contentplaceholder是在pagemaster中定义的,在pagearticlecategory中我并没有替换这个contentplaceholder,这些是在最终的页面中替换的。因为我在当前请求的上下文中全局注册了这个contentplaceholder控件所以实现了隔代继承,由于是全局的所以通常IsGlobal仅用在页面母版的占位符中。

c.支持ContentPlaceHolder嵌套

之所以支持ContentPlaceHolder嵌套是为了尽量减少系统中母版的数量,如上面的PageMaster中的以下代码:
                        <tr>
                            
<td valign="top" style="border-bottom: solid 1 White; height:200px">
                                
<KSS:ContentPlaceHolder ID="cphMain" runat="server" IsGlobal="true">
                                    
<table cellspacing="0" cellpadding="0" border="0" style="width: 100%; ">
                                        
<tr>
                                            
<td valign="top" style="border-bottom: solid 1 White; padding:5px;">
                                                
<KSS:ContentPlaceHolder ID="cphContent" runat="server" IsGlobal="true">
                                                
</KSS:ContentPlaceHolder>
                                            
</td>
                                            
<td class="helpPanel" valign="top" style="border-bottom: solid 1 White;">
                                                
<KSS:ContentPlaceHolder ID="cphHelp" runat="server" IsGlobal="true">
                                                
</KSS:ContentPlaceHolder>
                                            
</td>
                                        
</tr>
                                    
</table>
                                
</KSS:ContentPlaceHolder>
                            
</td>
                        
</tr>
我的cphMain这个占位符下面内含了两个占位符cphContent和cphHelp之所以这样作是因为有的时候我希望页面是三栏的,如上面的主页和内容页所示但有的时候我希望页面是两栏的如下图所示:
 
这样定义模板时,我只要定义一个模板,只要在替换时三栏替换cphContent和cphHelp而两栏替换cphMain就可以了。
下面是masterControl动态链接库,个人用户可以免费无限制使用。
/Files/keyss/Keyss.MasterControl.rar,另外你也可以到我的个人网站上体验上面的示例。