在Asp.net 1.1下实现MasterPage

   Asp.net 2.0 , 提供了一个 MasterPage 的功能 , 它可以让我们很方便的完成页面的整体结构相同的网站 , 而且后期修改界面的时候只要修改一下 MasterPage 即可 , 无需一个个界面进行修改 , 这样就大大的方便了开发人员。
    可惜的是,在 Asp.net 2.0 以前的版本中,并不包含 MasterPage 的特性。虽然现在使用 Asp.net 2.0 或以上版本的开发者越来越多,但是常常由于项目周期长等原因,还有很大数量的开发人员使用 Asp.net 1.1 进行开发(比如我自己)。所以,虽然 Asp.net 2.0 发布这么长时间了,我们这些可怜的人还是无法应用。这不, .net 3.5 都出来了,呵呵,感觉自己越来越落后了。
   网上看到过别人在 .net 1.1 下实现类似 MasterPage 的功能,但感觉都不是很直观,而且用法和 .net 2.0 下的 MasterPage 相差比较大。前天翻开 Spring.net 来看,发现它的 Web 框架实现了 .net 1.1 下的 MasterPage ,而且他的用法和 .net 2.0 下的用法一样。自己也照着葫芦画了个瓢,不敢私藏,特拿出来分享。
1 主要原理

根据Asp.net 2.0的MasterPage应用,我们知道:
1. MasterPage里面包含ContentPlaceHolder控件用做为具体内容的容器
2. 使用母版页的页面包含Content控件提供具体内容
3. 页面展现时,会将MasterPage的内容展现出来,并把Content控件下的内容填充到ContentPlaceHolder中。

仿照Asp.net 2.0,对应到Asp.net 1.1下面应该是:
1. 做两个自定义控件,分别叫做ContentPlaceHolder和Content
2. MasterPage是一个用户控件(ascx),里面包含ContentPlaceHolder控件做为具体内容的容器
3. 使用母版页的页面包含Content控件提供具体内容
4. 页面展现时,会将MasterPage的内容展现出来,并把Content控件下的内容填充到ContentPlaceHolder中。

是啊,就这么简单,没啥特别的东西,但没看到Spring.net之前,怎么就没想过去实现这么个MasterPage呢?

2 ContentPlaceHolder和Content控件

前面说了,ContentPlaceHolder是内容的容器,Content控件是具体的内容,他们的代码如下:

Content.cs:

 1 using  System.Web.UI;
 2 using  System.Web.UI.WebControls;
 3 namespace  ZSoft.Web.UI.WebControls
 4 {
 5      ///   <summary>
 6      ///  内容控件
 7      ///   </summary>

 8     [PersistChildren( true )]
 9     [ParseChildren( false )]
10      public   class  Content : Control
11      {
12          private   string  contentPlaceHolderID;
13
14          public   string  ContentPlaceHolderID
15          {
16              get   return  contentPlaceHolderID;}
17              set   { contentPlaceHolderID  =  value;}
18         }

19     }

20 }

21

 

可以看到,Content控件包含一个属性ContentPlaceHolderID,用以指向母版页的ContentPlaceHolder控件。

ContentPlaceHolder.cs:

 1 using  System.Web.UI;
 2 using  System.Web.UI.WebControls;
 3
 4 namespace  ZSoft.Web.UI.WebControls
 5 {
 6      ///   <summary>
 7      ///  内容容器
 8      ///   </summary>

 9     [PersistChildren( true )]
10     [ParseChildren( false )]
11      public   class  ContentPlaceHolder : WebControl
12      {
13          private  Content content;
14
15          public  Content Content
16          {
17              get   return  content;}
18              set   { content  =  value;}
19         }

20
21          protected   override   void  Render(HtmlTextWriter writer)
22          {
23              // 如果Content控件不为null,则展现Content控件的内容,否则,展现自己的内容
24              if (content  !=   null )
25              {
26                 content.RenderControl(writer);                
27             }

28              else
29              {
30                  base .Render (writer);
31             }

32         }

33     }

34 }

35

 

ContentPlaceHolder控件拥有一个属性 Content,指向一个 Content控件实例,在下面的代码中你会看到它是何时被赋值的。同时, ContentPlaceHolder重写了 Control控件的 Render方法,当它拥有一个 Content控件的实例的时候,展现 Content控件的内容,否则,展现自己的内容(用于展现默认内容)。
    注意 PersistChildren(true) ParseChildren(false),这两句指定了这两个控件是可以包含子控件的,这非常重要,因为不管是 ContentPlaceHolder,还是 Content控件,都需要拥有子控件( ContentPlaceHolder用子控件来表示默认的内容, Content用子控件表示具体的要替换的内容)。
    上面说到, ContentPlaceHolder控件拥有一个属性 Content,指向一个 Content控件实例,那么,这个实例是什么时候被赋值的呢?
    我们知道, MasterPage应该是一个用户控件,并且 ContentPlaceHolder控件是包含在 MasterPage控件里的,所以,我们应该在 MasterPage里去初始化 ContentPlaceHolder Content属性。在页面初始化时,根据页面的 MasterPageFile属性,加载 MasterPage控件,然后初始化该控件里的 ContentPlaceHolder。这样,我们就需要另外两个类, MasterPage基类和 Page基类,分别对应母版控件和使用母版的页面。
3 Page和MasterPage

Page.cs:

 1 using  System;
 2 using  System.Web.UI;
 3
 4 namespace  ZSoft.Web.UI
 5 {
 6      ///   <summary>
 7      ///  页面基类
 8      ///   </summary>

 9      public   class  Page : System.Web.UI.Page
10      {
11          private   string  masterPageFile;
12          private  MasterPage master;
13         
14          ///   <summary>
15          ///  母版的路径
16          ///   </summary>

17          public   string  MasterPageFile
18          {
19              get   return  masterPageFile;}
20              set   { masterPageFile  =  value;}
21         }

22
23          ///   <summary>
24          ///  是否有母版
25          ///   </summary>

26          public   bool  HasMaster
27          {
28              get   return   this .MasterPageFile  !=   null   ||   this .master  !=   null ;}
29         }

30         
31          protected   override   void  OnInit(EventArgs e)
32          {
33              if (HasMaster)
34              {
35                  // 加载母版并初始化母版
36                 master  =  (MasterPage)LoadControl(MasterPageFile);
37                 master.Initialize( this );
38             }

39              base .OnInit (e);
40         }

41
42          protected   override   void  Render(HtmlTextWriter writer)
43          {
44              // 有母版的时候,展现母版的内容
45              if (HasMaster  &&  master  !=   null )
46              {
47                 master.RenderControl(writer);
48             }

49              else
50              {
51                  base .Render (writer);
52             }

53         }

54     }

55 }

56

Page类重写了OnInit方法,并在OnInit时,调用MasterPage类的Initialize方法初始化母版。另外,它重写了Render方法,当母版存在的时候,展现母版的内容。

MasterPage.cs:

 1 using  System;
 2 using  System.Web.UI;
 3 using  ZSoft.Web.UI.WebControls;
 4
 5 namespace  ZSoft.Web.UI
 6 {
 7      ///   <summary>
 8      ///  母版(for asp.net 1.1 only)
 9      ///   </summary>

10      public   class  MasterPage : UserControl
11      {
12          ///   <summary>
13          ///  初始化母版
14          ///   </summary>
15          ///   <param name="childPage"></param>

16          public   void  Initialize(Page childPage)
17          {            
18              this .ID  =   " MasterPage " ;
19              for ( int  i  =   0 ; i  <  childPage.Controls.Count; i ++ )
20              {
21                  if (childPage.Controls[i]  is  Content)
22                  {
23                     Content content  =  childPage.Controls[i]  as  Content;
24
25                     ContentPlaceHolder holder  =  (ContentPlaceHolder) this .FindControl(content.ContentPlaceHolderID);
26
27                      if (holder  ==   null )
28                      {
29                          throw   new  ArgumentException( " 在母版页中未找到Content PlaceHolder  "   +  content.ContentPlaceHolderID);
30                     }

31
32                     holder.Content  =  content;
33                 }

34             }

35
36             childPage.Controls.AddAt( 0 , this );
37         }

38     }

39 }

40

 

在母版的初始化方法里,它遍历了子页面的第一层控件来寻找 Content控件,然后根据 Content控件实例的 ContentPlaceHolderID属性,从自身找到相对应的 ContentPlaceHolder控件,然后把 Content控件的实例赋值给 ContentPlaceHolder控件,从而达到初始化的目的,最后,母版把自己做为一个控件,加到子控件里( childPage.Controls.AddAt(0,this),这句话非常重要,少了这句会带来 PostBack时的异常。
    注意,上面的初始化方法,只是遍历了子页面的第一层控件来寻找 Content控件,这就要求我们的子页面(即使用母版的页面)的 Content控件不能放在 runat=server Form内了,因为如果控件位于 runat=server form内,页面的第一层控件里就遍历不到 Content控件了,因为他们属于 HtmlForm控件的子控件。当然,如果您非要在子控件的 Content控件外层放置一个 runat=server form的话,那就要修改一下上面的这段代码了。
    到这里为止,这个 MasterPage的功能就被我们实现了,代码比较简单,下面简单介绍一下如何使用。
4 如何使用

    它的使用方法和Asp.net 2.0下的MasterPage使用方法一样。
    首先我们定义一个母版页,后台代码继承与上面定义的基类MasterPage:

 1 <% @ Control Language = " c# "  AutoEventWireup = " false "  Codebehind = " Master.ascx.cs "  Inherits = " TaskManager.TestMaster.Master "
 2     TargetSchema = " http://schemas.microsoft.com/intellisense/ie5 "  
%>
 3 <% @ Register Assembly = " ZSoft.Web "  TagPrefix = " zsoft "  Namespace = " ZSoft.Web.UI.WebControls "   %>
 4 < html >
 5 < head >
 6      < title >
 7          < zsoft:ContentPlaceHolder  ID ="Title"  runat ="server"   />
 8      </ title >
 9 </ head >
10 < body >
11      < form  id ="Form1"  runat ="server" >
12          < table  width ="100%"  border ="1" >
13              < tr >
14                  < td >
15                      < h1 >
16                          < zsoft:ContentPlaceHolder  ID ="Head"  runat ="server" >
17                             Default Head
18                          </ zsoft:ContentPlaceHolder >
19                      </ h1 >
20                  </ td >
21              </ tr >
22              < tr >
23                  < td >
24                      < zsoft:ContentPlaceHolder  ID ="Content"  runat ="server"   />
25                  </ td >
26              </ tr >
27              < tr >
28                  < td  align ="center" >
29                     the footer
30                  </ td >
31              </ tr >
32          </ table >
33      </ form >
34 </ body >
35 </ html >
36

 

    在这个母版里,定义了三个ContentPlaceHolder,分别表示页面的Title,Head和Content。

    然后定义一个使用该模板的子页面(后台代码继承与上面定义的基类Page):

 1 <% @ Page Language = " c# "  Codebehind = " WebForm1.aspx.cs "  AutoEventWireup = " false "  Inherits = " TaskManager.TestMaster.WebForm1 "   %>
 2
 3 <% @ Register Assembly = " ZSoft.Web "  TagPrefix = " zsoft "  Namespace = " ZSoft.Web.UI.WebControls "   %>
 4 <! DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"  >
 5 < html >
 6 < head >
 7      < title > WebForm1 </ title >
 8      < meta  name ="GENERATOR"  content ="Microsoft Visual Studio .NET 7.1" >
 9      < meta  name ="CODE_LANGUAGE"  content ="C#" >
10      < meta  name ="vs_defaultClientScript"  content ="JavaScript" >
11      < meta  name ="vs_targetSchema"  content ="http://schemas.microsoft.com/intellisense/ie5" >
12 </ head >
13 < body  ms_positioning ="GridLayout" >
14      < zsoft:Content  ID ="Content1"  ContentPlaceHolderID ="Title"  runat ="server" >
15         Hello
16      </ zsoft:Content >
17      < zsoft:Content  ID ="Content2"  ContentPlaceHolderID ="Head"  runat ="server" >
18         Asp.net 1.1中的母版页
19      </ zsoft:Content >
20      < zsoft:Content  ID ="Content3"  ContentPlaceHolderID ="Content"  runat ="server" >
21          < asp:TextBox  ID ="TxtContent"  runat ="server"  zvalidate ="notnull(sdfsdf)"  zbind ="Content" ></ asp:TextBox >
22          < asp:Button  ID ="BtnSave"  runat ="server"  OnClick ="SaveClick"  Text ="submit"  zCausesValidation ="true"   />
23      </ zsoft:Content >
24 </ body >
25 </ html >
26

    这个页面定义了三个Content控件,分别对应与母版的三个ContentPlaceHolder控件。在该页面的后台代码类的OnInit方法里,加入this.MasterPageFile = "Master.ascx";用以指定母版文件,如下:

 1          override   protected   void  OnInit(EventArgs e)
 2          {
 3              this .MasterPageFile  =   " Master.ascx " ;
 4
 5              //
 6              //  CODEGEN: 该调用是 ASP.NET Web 窗体设计器所必需的。
 7              //
 8             InitializeComponent();
 9              base .OnInit(e);
10         }

11         
 

关于这个 MasterPageFile属性值的指定,这里是在 OnInit方法里硬编码赋值的,您也可以通过额外的方式(如配置文件, Spring的依赖注入等)来实现以提高灵活性,当然这些不属于我们讨论的内容。

我还尝试着扩展 Page指令,使 MasterPageFile属性可以像 Asp.net 2.0那样,通过 Page指令来设置,如:

1 <% @ Page Language = " c# "  MasterPageFile = " Master.ascx "  Inherits = " TaskManager.TestMaster.WebForm1 "   %>

    但不幸的是, .net 1.1并不能像 2.0那样可以在 Page指令里指定 Page中属性的值,最终放弃了这个想法。如果哪位朋友知道如何扩展 .net 1.1下的 Page指令,希望能告诉我,不胜感激。
    当然,如何能像 VS 2005的窗体设计器那样支持 MasterPage,有兴趣的朋友可以做更深一步的研究。

 

转:http://www.cnblogs.com/northdevil/articles/1156293.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值