Page类与Control类的生命周期(life cycle)比较总结[转]

最近一段时间在使用Microsoft SharePoint2007,进行开发一个Document管理系统,里面有使用SharePoint Designer设计Page布局,需要使用自定义开发的Webpart控件,由于本来就没有进行搞控件开发了,再加上有句名言“知识就是力量”,那没得知识就没得力量了,所以没得办法,就利用Baidu + Google,googling了半下午。结果再次验证了Google的搜索实力确实比Baidu强悍啊,在此为Google最近的决定感到失望,牢骚就不多发了,还是进正题。以下是自己的一些新的总结和找到的一些学习资源。希望可以share.

首先了解到,Page类继承自模板控件类TemplateControl Http处理程序接口IHttpHandler

1.TemplateControl类是个abstract类,继承自Control类,ID命名容器控件INamingContainer,

和设备筛选器接口IFilterResolutionService

2. IHttpHandler接口是为了使用Http处理程序同步处理Http Web请求而实现的协定。有一个只SetIsReusable属性和一个方法ProcessRequest(HttpContext context)方法,用来处理Http Web请求。

     既然Page间接的继承自Control类,那下面就先从介绍Control类说起。

Control类的生命周期有七个大的事件阶段,分别为Init事件,Load事件,DataBinding事件,PreRender事件,Render方法,Unload事件,Disposed事件。下面简单介绍下每个阶段;

1.Init阶段,引发Init事件,执行OnInit(EventArgs e)虚方法。详细请参见处理继承的事件。

2.然后执行TrackViewState(object savedState)虚方法,会根据页面IsPostBack属性,来确定是否LoadViewState(objcet savedState)来加载视图状态,以及如果控件继承自接口IPostBackDataHandler,还会实现LoadPostData(string postDataKey,System.Collections.Specialized.NameValueCollection postCollection),来处理回发数据。详细请参见处理回发数据维护控件中的状态

3.Load阶段,引发Load事件,执行OnLoad(EventArgs e)虚方法。此时,Load事件树中的服务器控件已创建并初始化、状态已还原并且窗体控件反映了客户端的数据。详细参见处理继承的事件。

4.之后根据页面IsPostBack属性,如果实现了接口IPostBackDataHandler则调用RaisePostDataChangedEvent() 方法,来处理引发更改事件以响应当前和以前回发之间的状态更改。如果实现如果已实现 IPostBackEventHandler接口,则引发RaisePostBackEvent(string eventArgument)方法的实现来处理,来处理引起回发的客户端事件,并在服务器上引发相应的事件。详细参见处理回发数据捕获回发事件。

5.PreRender阶段,引发PreRender 事件,执行OnPreRender(EventArgs e)方法,在呈现输出之前执行任何更新。可以保存在预呈现阶段对控件状态所做的更改,而在呈现阶段所对的更改则会丢失。此阶段是对控件做任何更新的最后机会。详细请参见处理继承的事件。

6.SaveViewState阶段,在此阶段后,自动将控件的 View State 属性保持到字符串对象中。此字符串对象被发送到客户端并作为隐藏变量发送回来。为了提高效率,控件可以重写 SaveViewState() 方法以修改 View State属性。详细请参见维护控件中的状态。

7.呈现Render阶段,此阶段没有不适事件,执行Render()方法,来生成呈现给客户端的输出。详细参见呈现 ASP.NET 服务器控件

8.Dispose阶段,执行Disponse()方法,执行销毁控件前的所有最终清理操作。在此阶段必须释放对昂贵资源的引用,如数据库链接。详细参见ASP.NET 服务器控件中的方法

9.卸载Unload阶段,引发Unload事件,执行OnUnload()方法,来执行销毁控件前的所有最终清理操作。控件作者通常在 Dispose 中执行清除,而不处理此事件。

 

以下2点要有所注意:   

1.要重写 Event Name 事件,请重写 OnEventName 方法(并调用 base.OnEventName)。

2.之上的方法和事件基本在自己创建控件时都可以重写 System.Web.UI.Controlabstract方法,但可以看出以下几个除外:LoadPostData 和 RaisePostDataChangedEvent 是 IPostBackDataHandler 接口的方法,而 RaisePostBackEvent 属于 IPostBackEventHandler 接口。如果控件参与回发数据处理,则必须实现 IPostBackDataHandler。如果控件收到回发事件,则必须实现 IPostBackEventHandler

3.以上没有列出 CreateChildControls 方法,这是由于每当 ASP.NET 页框架需要创建控件树时就会调用该方法,且该方法调用并不限于控件生命周期的特定阶段。例如,可以在加载页时、在绑定数据过程中或者在呈现过程中调用 CreateChildControls,以及在动态创建控件的时候也可以调用的。

下面给个自己创建个WebPart控件示范,功能比较简单,在页面上面实现Ifame实现内嵌页面的效果。

 

  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Text;  
  4. using System.Web;  
  5. using System.Web.UI;  
  6. using System.Web.UI.HtmlControls;  
  7. using System.Web.UI.WebControls.WebParts;  
  8.   
  9. namespace IframeWebpart  
  10. {  
  11.     public class Iframe:WebPart,INamingContainer  
  12.     {  
  13.         private int _width = 100;  
  14.         private int _height = 100;  
  15.         private HtmlTableCell _htc = new HtmlTableCell();  
  16.         private string _url = @"http://www.baidu.com";  
  17.   
  18.         [WebBrowsable(true),Personalizable(true)]  
  19.         public int IWidth  
  20.         {  
  21.             get { return _width; }  
  22.             set { _width = value; }  
  23.         }  
  24.   
  25.         [WebBrowsable(true),Personalizable(true)]  
  26.         public int IHeight  
  27.         {  
  28.             get { return _height; }  
  29.             set { _height = value; }  
  30.         }  
  31.   
  32.         [WebBrowsable(true), Personalizable(true)]  
  33.         public string Url  
  34.         {  
  35.             get { return _url; }  
  36.             set { _url = value; }  
  37.         }  
  38.   
  39.         protected override void CreateChildControls()  
  40.         {  
  41.             //base.CreateChildControls();  
  42.             this.Controls.Add(new LiteralControl("<table>"+"\n"));  
  43.             this.Controls.Add(new LiteralControl("<td>"+"\n"));  
  44.             this.Controls.Add(this._htc);  
  45.             this.Controls.Add(new LiteralControl("</td>"+"\n"));  
  46.             this.Controls.Add(new LiteralControl("</table>"+"\n"));  
  47.             if (!this.Page.IsPostBack)  
  48.             {  
  49.                 this.AddControls();  
  50.             }  
  51.   
  52.         }  
  53.   
  54.         protected override void OnLoad(EventArgs e)  
  55.         {  
  56.             base.OnLoad(e);  
  57.             //if (!this.Page.IsPostBack)  
  58.             //{  
  59.             //    this.AddControls();  
  60.             //}  
  61.         }  
  62.   
  63.         protected override void Render(HtmlTextWriter writer)  
  64.         {  
  65.             base.Render(writer);  
  66.         }  
  67.   
  68.         private void AddControls()  
  69.         {  
  70.             this._htc.Controls.Add(new LiteralControl("<iframe id='iframe1' src=""+this.Url+"" mce_src=""+this.Url+"" width='"+this.IWidth+"' height='"+this.IHeight+"'  marginheight='0' frameborder='0' scrolling='no' vspace='0' hspace='0'  marginwidth='0'  >"+"\n"));  
  71.             this._htc.Controls.Add(new LiteralControl("</iframe>"+"\n"));          
  72.         }  
  73.   
  74.     }  
  75. }  

 

 

 

Control类的比较简单,下面介绍Page类,Page类的生命周期相对control要稍微复杂一些。(下面介绍参考了MSDN上面的介绍)

普通的常规页面生命周期有:页请求,开始阶段,初始化,加载,验证,回发事件处理,呈现,卸载,除了页生命周期阶段以外,在请求前后还存在应用程序阶段,但是这些阶段并不特定于页。以下详细介绍;

1页请求:发生在页面生命周期之前,用户请求页时,ASP.NET将确定是否需要分析和编译页,从而确定是否开始页面的生命周期,或者是否可以在不运行页的情况下发送页面缓存以进行响应。

2开始:设置页属性,如:HttpContext以及其他属性;在此阶段,页面需要确定是回发请求还是新请求,并设置IsPostBack属性;设置页面的UICulture属性。

3页面初始化:加载所有主题;控件生成,并设置UniqueID

注:ViewStateControlState中的值还未加载至控件;如果页面是回发,则回发数据也还未加载;故此时控件可以访问,但值可能出错。

4加载:如果当前请求是回发请求,则为控件加载ViewStateControlState中的值。

5验证:调用所有验证程序控件的Validate方法,此方法将设置验证程序控件和页的IsValid属性。

6回发事件处理:如果请求是回发请求,则调用所有事件处理程序。

7呈现:首先对该页和所有控件进行保存视图状态,然后对每个控件调用Render方法,它会提供一个文本编写器,用于将控件的输入写入页的Response属性的OutputStream中。

8卸载:完成呈现,并已将页发送至客户端、准备丢弃该页后,调用卸载。将卸载属性如:ResponseRequest等等,故之后如果在调用这些对象,都将出错异常的。

 

在介绍Asp.net之前先来看两张关于页面的请求过程图片:

 

第一张是个普通的页面请求过程

第二张是个是个添加的Button按钮控件PostBack后的页面请求过程。

比较这两张图片可以很清晰的得出以下两点内容。

一. Asp.Net的页面生命周期,基本都有如下几个阶段;

PreInit

Init

InitComplete

PreLoad

Load

LoadComplete

PreRender

Render

RenderComplete

SaveState

SaveStateComplete

Render

UnLoad

二.在页面的响应生命周期阶段,控件的生命周期阶段也在相应同步进行着。

 

下面也来个粗略的介绍;

1.      PreInit阶段

此阶段引发PreInit事件,执行OnPreInit()方法,完成的操作,有比如查IsPostBack属性来确定是

不是第一次处理该页;创建或重新创建动态控件,动态设置主控页,动态设置Theme属性,读取或设置配置文件属性。注:如果请求是回发请求,则控件的值尚未从视图状态恢复,即:不应该在此事件中设置控件属性。

2.      Init阶段

在所有控件都已初始化,且应用了外观后由里到外引发Init事件,执行由自Control类继承得OnInit()方法,使用该事件来读取或初始化控件属性。

3.      InitComplete阶段

Page对象引发。使用该事件来处理要求先完成所有初始化工作的任务。执行OnInitComplete()方法。

4.  PreLoad

 Page 引发该事件后,执行OnPreLoad(),它会为自身和所有控件加载视图状态,然后会处理 Request 实例包括的任何回发数据,如果需要在 Load 事件之前对页或控件执行处理,请使用该事件。

5.  Load

PagePage上调用OnLoad事件方法,然后以递归方式对每个子控件执行相同操作,如此循环往复,直到加载完本页和所有控件为止。使用OnLoad()事件方法来设置控件中的属性并建立数据库连接。

6.  控件事件

使用这些事件来处理特定控件事件,如 Button 控件的 Click 事件等。注:在回发请求中,如果页包含验证程序控件,请在执行任何处理之前检查Page和各个验证控件的IsValid属性。

7.      LoadComplete

引发LoadComplete事件,执行OnLoadComplete()方法,对需要加载页上的所有其他控件的任务使用该事件。

8.      PreRender

引发PreRender事件,执行由Control类继承来的OnPreRender()方法,使用该事件对页或其控件的内容进行最后更改。注:在该事件发生前的操作:Page对所有控件递归进行EnsureChildControl操作设置了DataSourceID属性的数据绑定控件会调用DataBind方法。

9.  PreRenderComplete

   引发PreRenderComplete事件,执行的On PreRenderComplete方法。

10.  SaveStateComplete
引发SaveStateComplete事件,执行由OnSaveStateComplete(EventArgs e)方法,在该事件发生前,

已针对页和所有控件保存了ViewState。将忽略此时对页或控件进行的任何更改。。

11Render
这不是事件;在处理的这个阶段,Page 对象会在每个控件上调用此方法。所有 ASP.NET Web 服务

器控件都有一个用于写出发送给浏览器的控件标记的 Render 方法。如果创建自定义控件,通常要重写此

方法以输出控件的标记。不过,如果自定义控件只合并标准的 ASP.NET Web 服务器控件,不合并自定

义标记,则不需要重写 Render 方法。有关更多信息,请参见开发自定义 ASP.NET 服务器控件。用户

控件(.ascx 文件)自动合并呈现,因此不需要在代码中显式呈现该控件

12.Unload

引发Unload事件,执行由Control类继承来的OnUnLoad()方法,对子控件由里向外执行此方法。

 

注意点。当从 Page 类继承类时,除了可以处理由页引发的事件以外,还可以重写页的基类中的方法。例如,可以重写页的 InitializeCulture 方法,以便动态设置区域性信息。注意,在使用 Page_事件语法创建事件处理程序时,将隐式调用基实现,因此无需在方法中调用它。例如,无论是否创建 Page_Load 方法,始终都会调用页基类的 OnLoad 方法。但是,如果使用 override 关键字(在 Visual Basic 中为 Overrides)重写页的 OnLoad 方法,则必须显式调用基方法。例如,如果在页中重写 OnLoad 方法,则必须调用 base.Load(在 Visual Basic 中为 MyBase.Load)以运行基实现

 

以上就是本人的理解和总结,比较简单,可以说只有的理解了简单Control类和Page类的通用生命周期,以后才可以分析复杂的问题,比如动态创建控件时的数据丢失问题等。再比如目前有不少人喜欢在开发Web时候,比较喜欢直接让新建的Page页面继承自Page类,在这个继承来里面来写一些实现用户身份验证功能,但是如果对页面的生命周期比较了解后,可以这样来做,首先写个继承自Page类的类,在这个类里面根据需要来重写abstract方法和写一些通用的用户身份验证方法,之后让自己的页面后台代码类来实现这个类,从而达到通用的效果。这样做对代码复用性的提高,大家觉得是否有帮助呢?

 

以上拙见,如有错误之处,请帮忙指教。

 

 

参考资料

1.        MSDN文档ASP.NET 页生命周期概述

2.       《庖丁解牛:纵向切入ASP.NET 3.5控件和组件开发技术》

3.       Programming ASP.NET》学习笔记(控件)

4.       SnowQuery的专栏

5.       深入理解Asp.net动态控件

posted @ 2011-05-04 00:24  陳龑  阅读(517)  评论(0编辑  收藏  举报