ASP.NET页面生命周期 (转载)

#
 事件或方法
 功能
 描述
 
1
 Init 事件
 页面初始化
 页面生存周期中的第一个阶段是初始化。当 Init 事件发生时,在 .aspx 源文件中静态声明的所有控件都已实例化并取其默认值。应该注意的是,这时视图状态信息还不可用。
 
2
 LoadViewState 方法
 加载视图状态
 可以重写 LoadViewState 方法来自定义状态还原。
 
3
 LoadPostData 方法
 处理回发数据
 处理传入窗体数据。
 
4
 Load 事件
 加载页面
 页面控件初始化完成并反映了客户端的数据。
 
5
 RaisePostDataChangedEvent 方法
 回发更改通知
 引发更改事件。
 
6
 RaisePostBackEvent 方法
 处理回发事件
 处理引起回发的客户端事件,并在服务上引发相应时间。
 
7
 PreRender 事件
 页面预呈现
 各个控件可利用这个很好的时机,以便执行任何需要在保存视图状态和呈现输出结果的前一刻完成的最后一些更新操作。
 
8
 SaveViewState 方法
 保存视图状态
 将ViewState属性保存到字符串中,重写 SaveViewState 方法可以修改 ViewState 属性。
 
9
 Render 方法
 呈现页面
 重写 Render 方法,即可更改各个控件的呈现机制。该方法获取一个 HTML 编写器对象,并使用该对象聚集所有将针对该控件生成的 HTML 文本。Page 类的 Render 方法的默认实现方式包括对所有成员控件的递归调用。对于每个控件,页面都调用 Render 方法并将 HTML 输出放入高速缓存。
 
10
 Dispose 方法
 处置
 是否对昂贵资源的引用。
 
11
 Unload 事件
 卸载页面
 Unload 事件是一个页面的最后生存标志,该事件在页面对象被解除之前发生。在此事件中,您应该释放可能占用的任何关键资源(例如,文件、图形对象、数据库连接)。在此事件之后,浏览器收到 HTTP 响应数据包并显示页面。
 


当一个页面请求发送到WEB服务器时,不论该事件是由页面提交还是由页面重定向而激发的,页面在其被创建到释放的过程中都会运行一系列的事件。一个ASP.NET页面从被创建到释放的过程包含10个事件。
(1)对象初始化Init事件:页面初始化的标志是Init事件。页面中的控件(包括页面本身)都是在它们最初的Form中被首次初始化的。在成功创建页面的控件树后,对应用程序激发这个事件。当Init事件发生时,在.aspx源文件中静态声明的所有控件都以实例化并取其默认值。应该注意到,这是还没有视图状态信息可供使用。虽然可以重载OnInit方法,但是系统并不保证这些控件实例是按照怎样的顺序被创建的。
  (2)加载视图:在初始化之后,页面框架立即加载该页面的视图状态(ViewState)。所谓视图状态就是一些名称/值对的集合,例如可以保存TextBox控件的ID和Text属性值。它一般被用于在一个往返行程中存留信息到服务器,即参与HTTP请求与响应。

  页面视图状态被存储在<input type=”hidden”>字段中,做为_VIEWSTAE的值进行记录。该视图状态通过ASP.NE自动维护。通过重写LoadViewState方法组件,开发人员可控制如何还原视图状态以及如何将其内容影射到内部状态。LoadViewState方法就是从ViewState中获取上一次的状态,并按照页面的控件树的结构,用递归来遍历整个树,将对应的状态恢复到每一个控件上。

  (3)处理回发数据:还原了视图状态,页面树种的各个控件的状态就与浏览器上次呈现该页面时这些控件所处的状态相同。下一步需要更新这些控件的状态以发送给客户端。

  回发数据处理阶段是各个控件有机会更新其状态,以便准确的反映相应的HTML元素在客户端的状态。例如,一个服务器TextBox控件对应的HTML元素是<input type=text>,在回发数据阶段,TextBox控件将检索<input>标记的当前值并用它刷新其内部状态。每个控件负责从以发送的数据中提取相应值,并更新其某些属性。TextBox控件将更新Text属性,而CheckBox控件将刷新其Checked属性。服务器控件和HTML元素之间的匹配关系由二者的ID确定。

  页框架将在每个提交数据的控件上实现IpostBackDataHandler接口,然后激发LoadPostData事件,通过页面解析发现实现了IpostBackDataHandle接口的控件,这样就能正确的回传数据更新控件状态。在识别控件时,ASP.NET通过匹配控件的唯一标示符来更新正确的控件,该标识符具有名称值集和中的名称值对。这也就是在所有特定的页中每个控件都需要一个唯一标识符的原因之一。其他的步骤都由框架来完成,例如确定每个标识符在环境中是否唯一以及控件的基本属性等。

LostPostData方法的原型如下:
Public virtual bool LoadPostData(string postDatakey, NameValueCollection postCollection)

  PostDataKey是标识控件的关键字,可以理解为控件的ID,postCollection是包含回发数据的集合,可以理解为视图状态值。该方法返回一个bool值,如果是true,则表示控件状态因回发而更改;否则返回false。页框架会更跟踪所有返回true的控件并在这些控件上调用RaisePostDataChangeEvent事件。

  LoadPostData方法是由System..Web.WebControls.Control定义的,而添加的每一个服务器控件也是从System..Web.WebControls.Control继承的,所以对于数据的回发处理并不需要干预。

  (4)加载页面Load:在回发数据处理阶段结束时,页面中的所有控件都根据客户端上所输入的更改来更新的状态。此时,对页面激发OnLoad事件。对于这个事件,相信大多数朋友都会比较熟悉,用Visual Studio.Net生成的页面中的Page_Load方法就是响应Load事件的方法,对于每一次请求,Load事件都会触发,Page_Load方法也就会执行。可以利用该方法执行一些页面初始化,例如准备好数据库的连接字符串。在事件引用中,为了提高性能,通常使用Page类的IsPostBack属性判断是不是数据回发。

  (5)回发更改通知RaisePostDataChanged:如(3)所述,在所有实现了IpostBackDataHandler接口的控件被正确的回传数据更新后,每个控件都有一个布尔值的标识,标识其自上一次提交后改控件的数据是被更改还是保持其值。然后ASP.NET通过搜索页来寻找任何显示控件数据被更改的标识并激发RaisePostDataChanged。RaisePostDataChanged事件直到Load事件发生后,所有控件被更新后才激发。这保证了在控件被回传数据更新前,其他控件的数据在RaisePostDataChanged事件中没有被手动更改过。虽然也可以在Page的基础上自己定义数据更改的事件,但通常这个事件由太大用处。

  (6)处理回发事件RaisePostBackEvent:当回传更新导致数据改变而引发服务器端事件后,引发回传的对象会在RaisePostBackEvent事件中被处理。这种引发回传的对象往往是一个按钮被单击或者其状态改变而引发回传的控件。例如Button触发乐Onclick事件、客户端修改了某个文本框的文本、同时将AutoPostBack设置为true、触发TextChanged事件等。

  很多代码都在这个事件中执行,因为这是控制事件驱动逻辑的理想位置。为了保证呈现到浏览器的数据的正确性,在一系列的回传事件后,RaisePostBackEvent事件最终被激发。基于一致性考虑,会传中改变的控件直到这个函数被执行后才被更新。在实际的ASP.NET开发工作中要做的工作就是在此事件发生前处理代码。

  (7) 预呈现PreRender:在处理回发事件后,页面就准备进行呈现。这一阶段的标志是PreRender事件。各个控件可利用这个很好的时机,以便执行任何需要在保存视图状态和呈现输出结果的前一刻完成得最后一些更新操作。最终请求的处理都会转变为发挥服务器的响应,预呈现这个阶段就是执行在最终呈现之前所做的状态的更改,因为在呈现一个控件之前,必须更具它的属性来产生HTML,比如Style属性。这是典型的例子,这预呈现之前,可以更改一个控件的Style,当执行预呈现时,就可以把Style保存下来,做为呈现阶段显示HTML的样式信息。

  (8)保存状态SaveViewState:下一个状态为SaveViewState,在这一状态中所有控件以及页面本身可以刷新自己的SaveState集合的内容。所得到的视图状态随后得以序列化、进行哈希运算、进行Base64编码并关联到VI-EMSTATE隐藏自端。

  (9)呈现视图Render:到这里,实际上页面对请求的处理基本就告一段落了,在Render事件中,也调用对象是它们呈现为HTML,然后也收集HTML发送给客户。客户接收到HTML标记后进行重组,最终显示给客户。当Render事件被重载时,开发者可以为浏览器创建定值的HTML,此时页面创建的任何HTML都还没有生效。Render方法用HtmlTextWriter对象做参数并由它产生HTML送给浏览器。这主要用于自定义控件的开发。

  (10)处置Disposed:执行销毁控件前的所有最终清理操作。在此阶段必须释放对昂贵资源的引用,如内存的退出、数据库的连接等。

  (11)卸载Unload:一个页面的最后生存标志就是Unload事件,该事件在页面对象被解除之前发生。在此事件中,可以调用Dispose方法尽可能释放占用的任何关键资源(例如,文件、图形对象以及数据库连接)。


一个Page处理的过程讲解
Page类的派生关系
Page是从TemplateControl中派生的,TemplateControl从Control派生。他们的定义分别如下:
 //Control,从object派生,实现了四个接口
public class Control : System.ComponentModel.IComponent,
     System.IDisposable,
     System.Web.UI.IParserAccessor,
     System.Web.UI.IDataBindingsAccessor
{  }
 
//TemplateControl从Control派生,实现了接口System.Web.UI.INamingContainer
public abstract class TemplateControl : Control, System.Web.UI.INamingContainer
{  }
 
//Page从TemplateControl派生,实现了接口System.Web.UI. IHttpHandler
public class Page : TemplateControl, System.Web.IHttpHandler
{  }
 
而你编写的Page,你在_aspx.cs文件中编写的类是从Page派生,这个继承关系是你添加WebForm页面时,Visual Studio .net帮你建好的。而aspx的页面在运行时会被解释为一个从_aspx.cs文件中的类的派生类。(这里需要补充资料)。
 
正 题
猜想
当IIS接收到aspx页面一个请求,交给aspnet_isapi.dll处理。aspnet_isapi然后做以下的事情:
 
MyPage __p;
__p = new MyPage();
__p.ProcessRequest(System.Web.HttpContext.Current)
 
以上是我个人的估计,究竟事实是不是这样,我会找Microsoft的工程师要答案!我在System.Web.dll中没有找到处理产生Page实例的il,Page类的实例的创建可能是aspnet_isapi.dll作了,而aspnet_isapi.dll不是使用.net编写的,其内部的运行究竟如何无从得知。
 
以下不是猜想!
ProcessRequest(HttpContext)方法
Page中的ProcessRequest(System.Web.HttpContext)方法是这样的:
 
public virtual void ProcessRequest(Sunrise.Web.HttpContext context)
{
  this.SetIntrinsics(context); //进行最基本初始化
  this.ProcessRequest(); //处理请求
}
 
SetIntrinsics方法
从上面的代码可以看到,首先会调用SetIntrinsics(HttpContext context)方法,SetIntrinsics的作用是对Page的成员变量进行基本的初始化。SetIntrinsics如下:
 
//进行最基本初始化,设置_context、_request、_application、_cache的初值
private void  SetIntrinsics(HttpContext context)
{
     this._context = context;
     this._request =    context.Request;
     this._response = context.Response;
     this._application = context.Application;
     this._cache = context.Cache;
 
     if(this._clientTarget != null) {
         if(this._clientTarget.Length >0)  {
              this._request.ClientTarget = this._clientTarget;
         }
     }
      //调TempalateControl的HookUpAutomaticHandlers()方法初始化基本事件
     //包括:Init、Load、DataBinding、PreRender、Unload、Error、
     //AbortTransaction、CommitTransaction
     this.HookUpAutomaticHandlers();
}
 
ProcessRequest方法
然后就开始执行ProcessRequest()方法,大致如下:
try
{
     if(this.IsTransacted) //如果是事务状态
     {
         this.ProcessRequestTransacted();
     }
     else //非事务状态
     {
         this.ProcessRequestMain();
     }
     this.ProcessRequestEndTrace();
}
finally
{
     //释放资源,把_request、_response置为null,并执行UnloadRecursive(true)
     this.ProcessRequestCleanup();
}
 
ProcessRequestMain方法
通过上面的代码可以知道,当一个Page是非事务模式时,响应ProcessRequestMain()。事实上,是事务模式时,也是调用ProcessRequestMain()。以下是关于ProcessRequestMain()方法的伪码:
//step1 调用Control.InitRecursive()进行递归初始化 Control的OnInit()方法将会在此时被调用
this.InitRecursive(null);
 //如果是回传状态
if(this.IsPostBack)
{
     //step2 装载ViewState
     ///装载Page的ViewState,Control的LoadViewState()方法将会在这里被调用
     this.LoadPageViewState();
    //step3 处理回传的数据,如果Control实现了System.Web.UI.IPostBackDataHandler,
     //LoadPostData()方法在此时被调用
     this.ProcessPostData(this._requestValueCollection, true);
}
 
//step4 调用Control.LoadRecursive()方法装载SubControls
//Control的Load()方法在此时被调用
this.LoadRecursive();
 
//如果是回传状态
if(this.IsPostBack)
{
     //step 5 再次处理未处理完的回传数据,
     //注意第二次调用时第二个参数是false,而第一次是true
     //第二次处理回传数据的作用可能是用来处理在OnLoad中创建的Control的数据,??
     this.ProcessPostData(this._leftoverPostData, false);
 
     //step6 响应数据更改事件,
     //如果Control实现了System.Web.UI.IPostBackDataHandler接口,
     //Control的RaisePostDataChangedEvent()方法在此时被调用
     this.RaiseChangedEvents();
 
     //step7 响应回传事件 __dopostback()
     //如果Control实现了System.Web.UI.IPostBackEventHandler接口,Control的RaisePostBackEvent()方法在此时执行
     this.RaisePostBackEvent(this._requestValueCollection);
    //step8 调用Control的PreRenderRecursiveInternal()方法,
     //Control的OnPreRender()方法在此时被调用
     this.PreRenderRecursiveInternal();
}
 
//step9 BuiltTree 构建ViewState,
//ViewState使用通过LosFormatter对ViewState进行编码,保存在Trace中
this.BuildProfileTree("ROOT",this.EnableViewState);
 
//step10 保存Page的ViewState
//Control的SaveViewState()方法
this.SavePageViewState();
 
//step11 输出
//Control的Render()方法在此时被调用
this.RenderControl(this.CreateHtmlTextWriter(this.Response.Output));
 
ProcessRequestMain方法图示
Init
Load ViewState
Load PostData
Load
Load PostData
RaiseChangeEvents
RaisePostBackEvents
BuildProfileTree    (viewstate 编码)
SaveViewState
Render
Dispose
Unload
此时产生HTML代码
OnInit()方法被调用
OnLoad()方法被调用
处理回传数据
再次处理回传数据
响应数据修改事件,如TextBox的TextChagned
响应回传事件,客户端的__dopostback
ProcessRequestTransacted方法
事实上,在事务状态下,也是调用ProcessRequestMain()的,我们来看一下ProcessRequestTransacted的代码:
bool V_0;
System.Web.Util.TransactedCallback V_1;
 
V_0 = false;
 
//创建事务回调的delegate
V_1 = new System.Web.Util.TransactedCallback(this.ProcessRequestMain);
 
//在事务环境下调用ProcessRequestMain()
System.Web.Util.Transactions.InvokeTransacted(
     V_1,
     (System.EnterpriseServices.TransactionOption)(this._transactionMode),
     ref V_0
     );
if(V_0) {
     this.OnAbortTransaction(System.EventArgs.Empty); //终止事务
}
Else {
     this.OnCommitTransaction(System.EventArgs.Empty); //提交事务
}
 
InitRecursive方法
在ProcessRequestMain 方法中,首先执行Control的InitRecursive方法。在InitRecursive中,OnInit方法被执行。具体如下:
internal void InitRecursive(System.Web.UI.Control namingContainer) //ok
{
     string V_0;
     int V_1;
     int V_2;
     System.Web.UI.Control V_3;
 
     if(this._controls != null)
     {
//flag[128]表示是否实现了System.Web.UI.INamingContainer接口
         if(this.flags[128]) {
              namingContainer = this;
         }
          //设置_controls为只读,并返回原来的_readOnlyErrorMsg,保存在V_0
         V_0 = this._controls.SetCollectionReadOnly("Parent_collections_readonly");
          V_1 = this._controls.Count;
          for(V_2 = 0;V_2 < V_1; V_2++)
         {
              V_3 = this._controls[V_2];
              V_3._namingContainer = namingContainer;
              if(namingContainer != null)
              {
                   if((V_3._id == null) && (!(V_3.flags[64])) )
                   {
                       this.GenerateAutomaticID();
                   }
              }
               V_3._page = this._page;
              V_3.InitRecursive(namingContainer);
          }
          //回复_controls原来的状态
         this._controls.SetCollectionReadOnly(V_0);
          this._controlState = Sunrise.Web.UI.ControlState.Initialized;
          //OnInit方法在此时被调用
         this.OnInit(System.EventArgs.Empty);
         this.TrackViewState();
     }
}


LoadPageViewState方法
在ProcessRequestMain 方法中,执行InitRecursive方法进行初始化后,如果Page的IsPostBack属性值为True,则会接着执行LoadPageViewState方法。
 
LoadPageViewState应该结合来SavePageViewState来看,这样更容易理解。具体如下:
 
internal void LoadPageViewState()
{
     System.Web.UI.Triplet V_0;
     string V_1;
      //获得使用LosFormater反序列化(Deserialize)得到的对象
     V_0 = (System.Web.UI.Triplet)this.LoadPageStateFromPersistenceMedium();
 
     if(V_0 != null)
     {
         V_1 = (string)V_0.First;
         int.Parse(V_1,System.Globalization.NumberFormatInfo.InvariantInfo);
         this.GetTypeHashCode();
 
         //判断V_1和Page调用GetTypeHashCode()方法获得的值是否相等,
//并把比较结果保存在_fPageLayoutChanged ??
         this._fPageLayoutChanged = (int.Parse(V_1,System.Globalization.NumberFormatInfo.InvariantInfo) != this.GetTypeHashCode());
          if(!(this._fPageLayoutChanged))
         {
            //调用Control.LoadViewStateRecursive()方法,
//递归装载ViewState,
//Control的LoadViewState()方法将会在这里被执行
              this.LoadViewStateRecursive(V_0.Second);
              this._controlsRequiringPostBack = (System.Collections.ArrayList)V_0.Third;
         }
     }
}

LoadViewStateRecursive方法
通过观察LoadPageViewState的代码可以得知,LoadPageViewState中执行LoadViewStateRecursive来进行Control的LoadViewState过程。
 
LoadViewStateRecursive的过程大致是这样,把参数savedState转换为Triplet对象,使用Triplet对象的First做参数来执行LoadView方法,然后使用Triplet对象的Second和Third对Controls进行LoadViewStateRecursive。
 
这个方法应该参照SaveViewStateRecursive一起阅读,这样更方便理解。如下:
internal void LoadViewStateRecursive(object savedState)
{
     System.Web.UI.Triplet V_0;
     System.Collections.ArrayList V_1;
     System.Collections.ArrayList V_2;
     Sunrise.Web.UI.ControlCollection V_3;
     int V_4;
     int V_5;
     int V_6;
     int V_7;
 
     //如果参数为null或EnableViewState为False
     if((savedState == null) || (this.flags[4]) ))
//flags[4]表示EnableViewState: true时,EnableViewState为false,false时EnableViewState为true
      {         return;     }
 
     //转换为Triplet对象,Triplet有三个public的Fields:First、Second、Third,使用Triplet表示使代码更直观,而且更快
     V_0 = (System.Web.UI.Triplet)savedState;
 
     //如果control的Page不为null,而且this.Page.IsPostBack为True,执行LoadViewState方法
     if((this.Page != null) && (this.Page.IsPostBack) )
     {         this.LoadViewState(V_0.First);     }
      //对this.Controls中的Control进行LoadViewState
     if(V_0.Second != null)
     {
         V_1 = (System.Collections.ArrayList)V_0.Second; //kes
         V_2 = (System.Collections.ArrayList)V_0.Third; //values
         V_3 = this.Controls;
         V_4 = V_3.Count;
         V_5 = V_1.Count;
 
         for(V_6=0; V_6 < V_5; V_6 ++)
         {
              V_7 = (int)V_1[V_6];
              if(V_7 < V_4)
              {                   V_3[V_7].LoadViewStateRecursive(V_2[V_6]);              }
              else
              {
                   if(this._controlsViewState == null)
                   {
                       this._controlsViewState = new System.Collections.Hashtable();
                   }
                   this._controlsViewState[V_7] = V_2[V_6];
              }
         }
     }
      //将_controlState设置为ViewStateLoaded
     this._controlState = Sunrise.Web.UI.ControlState.ViewStateLoaded;
}

LoadViewState方法
       LoadViewState方法的是Protected,当你构建自己的Control时,可以重载它。此时你应该在你写的LoadViewState方法里执行base. LoadViewState(something)。Control的LoadViewState代码如下:
 
protected virtual void  LoadViewState(object savedState)
{
     object V_0;
      if(savedState != null)
     {
         this.ViewState.LoadViewState(savedState);
         V_0 = this.ViewState["Visible"];
 
         if(V_0 != null)
         {
              // flags[16]表示Visible,false时,Visible为true,值为false时,Visible为true
              this.flags[16] = (!((bool)V_0));
              this.flags[32] = true; // flags[32]表示是否已经LoadView ??
         }
     }
}

ProcessPostData方法
当ProcessRequestMain执行了LoadPageViewState方法后,接着就是ProcessPostData,这个方法是用来处理回传数据。这个方法被执行两遍,分别在LoadRecursive之前和之后个执行一遍。第一次执行时,参数fBeforeLoad值为True,第二次为False。
//处理回传的数据
//当fBeforeLoad为true时,是第一次调用,fBeforeLoad为false时,为第二次调用
private void ProcessPostData(System.Collections.Specialized.NameValueCollection postData, bool fBeforeLoad) //ok
{
     string V_0;
     Sunrise.Web.UI.Control V_1;
     System.Web.UI.IPostBackDataHandler V_2;
     bool V_3;
     System.Collections.ArrayList V_4;
     string V_5;
     System.Web.UI.IPostBackDataHandler V_6;
     bool V_7;
     System.Collections.IEnumerator V_8;
     System.IDisposable V_9;
 
     if(this._changedPostDataConsumers == null)
     {
         this._changedPostDataConsumers = new System.Collections.ArrayList();
     }
     if(postData != null)
     {
         V_8 = postData.GetEnumerator();
          try
         {
              while(V_8.MoveNext())
              {
                   V_0 = (string)V_8.Current;
                    if(V_0 != null)
                   {
                       //判断回传数据是否在系统PostFields内,
                       //s_systemPostFields包括__EVENTTARGET、__EVENTARGUMENT、__VIEWSTATE
                       if(!s_systemPostFields.Contains(V_0))
                       {
                            V_1 = this.FindControl(V_0); //在Page中查找匹配的Control
                             if((V_1 == null) //如果找不到,将数据保存在_leftoverPostData中
                                 && (fBeforeLoad) )
                            {
                                 if(this._leftoverPostData == null)
                                 {
                                     this._leftoverPostData = new System.Collections.Specialized.NameValueCollection();
                                 }
                                  this._leftoverPostData.Add(V_0,null);
                            }
                            else //如果找到了……
                            {
                            //如果找到的结果实现了System.Web.UI.IPostBackDataHandler接口,
                            //调用RegisterRequiresRaiseEvent方法
//赋值给_registeredControlThatRequireRaiseEvent
                                 if(!(V_1 is System.Web.UI.IPostBackDataHandler))
                                 {
                                     if(V_1 is System.Web.UI.IPostBackEventHandler)
                                     {
                                          this.RegisterRequiresRaiseEvent(
                                               (System.Web.UI.IPostBackEventHandler)V_1
                                               );
                                     }
                                 }
                                 else//如果找到的结果没有实现System.Web.UI.IPostBackDataHandler接口
                                 {
                                     V_2 = (System.Web.UI.IPostBackDataHandler)V_1;
                                     V_3 = V_2.LoadPostData(V_0,this._requestValueCollection); //V_2装载回传数据
                                     if(V_3) //如果V_2装载回传数据时发现数据已经被修改,将登记V_2在_changedPostDataConsumers中
                                     {
                                          this._changedPostDataConsumers.Add(V_2);
                                     }
 
                                     //在_controlsRequiringPostBack中移去V_0
                                     //_controlsRequiringPostBack的值在LoadPageViewState()方法中被赋值
                                     if(this._controlsRequiringPostBack != null)
                                     {
                                          this._controlsRequiringPostBack.Remove(V_0);
                                     }
                                 }
                            }
                       }
                   }
              }
         }
         finally
         {
              V_9 = V_8 as System.IDisposable;
              if(V_9 != null)
              {
                   V_9.Dispose();
              }
         }
     }
     V_4 = null;
 
     //处理_controlsRequiringPostBack中剩下的数据项
     if(this._controlsRequiringPostBack != null)
     {
         V_8 = this._controlsRequiringPostBack.GetEnumerator();
         try
         {
              while(V_8.MoveNext())
              {
                   V_5 = (string)V_8.Current;
                   V_6 = (System.Web.UI.IPostBackDataHandler)this.FindControl(V_5);
                    if(V_6 != null)
                   {
//V_7装载回传数据
                       V_7 = V_6.LoadPostData(V_5,this._requestValueCollection);
 //如果V_6装载回传数据时发现数据已经被修改,
//将登记V_6在_changedPostDataConsumers中
                       if(V_7) {
                            this._changedPostDataConsumers.Add(V_6);
                       }
                   }
                   else
                   {
                       if(fBeforeLoad) //如果是第一次调用,保留
                       {
                            if(V_4 == null)
                            {
                                 V_4 = new System.Collections.ArrayList();
                            }
                            V_4.Add(V_5);
                       }
                   }
              }
         }
         finally
         {
              V_9 = V_8 as System.IDisposable;
              if(V_9 != null)
              {
                   V_9.Dispose();
              }
         }
     }
 
//如果是第一次调用,值为未处理的数据项,第二次调用值为null
     this._controlsRequiringPostBack = V_4;
}

LoadRecursive方法
LoadRecursive方法的过程大致是这样,先执行Load方法,然后对Controls执行LoadRecursive。
 代码如下:
internal void LoadRecursive()
{   string V_0;
     int V_1;
     int V_2;
      //在这里执行OnLoad方法
     this.OnLoad(System.EventArgs.Empty);
 
     if(this._controls != null)
     {
         //设置_controls为只读
         V_0 = this._controls.SetCollectionReadOnly("Parent_collections_readonly");
         V_1 = this._controls.Count;
         for(V_2 = 0;V_2 < V_1; V_2++)
         {       this._controls[V_2].LoadRecursive();        }
         //回复原来的状态
         this._controls.SetCollectionReadOnly(V_0);
     }
      this._controlState = Sunrise.Web.UI.ControlState.Loaded;
}

RaiseChangedEvents方法
如果Control实现了System.Web.UI.IPostBackDataHandler接口,Control的RaisePostDataChangedEvent将会在这里被调用。需要的响应的RaisePostDataChangedEvent的典型例子是TextBox。TextBox的Text数据在客户端可能被修改,如果修改了,需要获得通知,以响应TextChanged事件。
 
internal void RaiseChangedEvents() //ok
{
     int V_0;
     System.Web.UI.IPostBackDataHandler V_1;
     Sunrise.Web.UI.Control V_2;
 
     if(this._changedPostDataConsumers != null)
     {
         for(V_0=0; V_0 < this._changedPostDataConsumers.Count; V_0++)
          {
              V_1 = (System.Web.UI.IPostBackDataHandler)this._changedPostDataConsumers[V_0];
              V_2 = V_1 as Sunrise.Web.UI.Control;
               //如果V_2是Page的SubControls,调用RaisePostDataChangedEvent()
              if((V_2 != null) || (V_2.IsDescendentOf(this)))
              {     V_1.RaisePostDataChangedEvent();       }
         }
     }
}

RaisePostBackEvent方法
 当执行Client端JavaScript的__dopostback()函数时,将产生回传事件。(此处需要补充资料)。回传事件的原理是,Page产生的HTML代码中,会产生两个隐藏域(Hidden),__EVENTTARGET和__ EVENTARGUMENT,不同的Control产生执行__dopostback()的方法不一致。调用的时候大多数是这样:
 
<input type=hidden name=__EVENTTARGET>
<input type=hidden name=__ EVENTARGUMENT>
__dopostback(control_uniqueid, ‘’);
 
当Form提交的时候,RaisePostBackEvent中根据control_uniqueid来找到需要响应事件的Server Control。__ EVENTARGUMENT是事件的参数,参数在开发简单的Server Control通常是不需要,在复杂的Server Control时才用。
例如System.Web.UI.WebControls.LinkButton。
 
private void RaisePostBackEvent(System.Collections.Specialized.NameValueCollection postData)
{
     string V_0;
     Sunrise.Web.UI.Control V_1;
     string V_2;
 
     if(this._registeredControlThatRequireRaiseEvent != null)
     {
         this.RaisePostBackEvent(_registeredControlThatRequireRaiseEvent, null);
         return;
     }
     V_0 = postData["__EVENTTARGET"];
     if((V_0 != null) && (V_0.Length >0))
     {
         V_1 = this.FindControl(V_0);
         if((V_1 != null) && (V_1 is System.Web.UI.IPostBackEventHandler) )
         {
              V_2 = postData["__EVENTARGUMENT"];
              this.RaisePostBackEvent((System.Web.UI.IPostBackEventHandler)V_1, V_2);
         }
     } else  {
         this.Validate();
     }
}

PreRenderRecursiveInternal方法
       Control的PrenRender方法会在这里被执行。代码如下:
internal void PreRenderRecursiveInternal()
{
     string V_0;
     int V_1;
     int V_2;
      // flags[16]表示Visible,值为true时,Visible为false,值为false时,Visible为true
     if(!(this.flags[16]))
     {
         this.EnsureChildControls();
          //OnPreRender在这里被执行
         this.OnPreRender(System.EventArgs.Empty);
          if(this._controls != null)
         {
              //设置只读
              V_0 = this._controls.SetCollectionReadOnly("Parent_collections_readonly");
               V_1 = this._controls.Count;
               for(V_2 = 0;V_2 < V_1; V_2++)
              { this._controls[V_2].PreRenderRecursiveInternal(); }
              //回复原来的状态
              this._controls.SetCollectionReadOnly(V_0);
         }
     }
     this._controlState = Sunrise.Web.UI.ControlState.PreRendered;
}

BuildProfileTree 方法
构建ViewState的存放空间,放在Trace里。(此处需要更详细的说明)
protected void BuildProfileTree(string parentId, bool calcViewState)
{
     int V_0;
     int V_1;
     int V_2;
     calcViewState = calcViewState || (!(this.flags[4]));
     if(calcViewState)
     {
         //计算LosFormatter会产生编码结果的长度
         V_0 = Sunrise.Web.UI.LosFormatter.EstimateSize(this.SaveViewState());
     }  else   {
         V_0 = 0;
     }
      this.Page.Trace.AddNewControl(
         this.UniqueID,
         parentId,
         this.GetType().FullName,
         V_0
         );
     if(this._controls != null)
     {
         V_1 = this._controls.Count;
         for(V_2 =0; V_2 < V_1; V_2 ++)
         { this._controls[V_2].BuildProfileTree(this.UniqueID, calcViewState); }
     }
}
SavePageViewState 方法
保存Page的ViewState,LosFormatter的编码结果放置于_viewStateToPersist中。此方法应该结合LoadPageViewState一起来分析。
使用Triplet和Pair保存ViewState时常用的技巧。这里使用了Triplet。
(需要补充关于LosFormatter的说明)
SavePageViewState和SavePageStateToPersistenceMedium的代码如下:
 internal void SavePageViewState()
{
     System.Web.UI.Triplet V_0;
     int V_1;
     if(!(this._needToPersistViewState))
     {  return;   }
     V_0 = new System.Web.UI.Triplet();
     V_1 = this.GetTypeHashCode();
     //将数字转换为字符
     V_0.First = V_1.ToString(System.Globalization.NumberFormatInfo.InvariantInfo);
     //_registeredControlsThatRequirePostBack是一个ArrayList,
     //其内容项 RegisterRequiresPostBack(System.Web.UI.Control control)来添加
     V_0.Third = this._registeredControlsThatRequirePostBack;
     if(this.Context.TraceIsEnabled)
     {
         this.Trace.AddControlViewstateSize(
              this.UniqueID,
              Sunrise.Web.UI.LosFormatter.EstimateSize(V_0)
              );
     }
//执行SaveViewStateRecursive()方法,把返回结果保存赋给V_0.Second。
     V_0.Second = this.SaveViewStateRecursive();
     this.SavePageStateToPersistenceMedium(V_0);
}

protected virtual void SavePageStateToPersistenceMedium(object viewState)
{
     this._viewStateToPersist = viewState;
}

SaveViewStateRecursive方法
在SavePageViewState方法中执行SaveViewStateRecursive方法来获取ControlTree的SaveViewState的结果。
 
internal object SaveViewStateRecursive()
{
     object V_0;
     System.Collections.ArrayList V_1;
     System.Collections.ArrayList V_2;
     int V_3;
     int V_4;
     Sunrise.Web.UI.Control V_5;
     object V_6;
     System.Web.UI.Triplet V_7;
 
     if(this.flags[4])
     {   return(null);   }
 
     //获得Control本身的SaveViewState()结果
     V_0 = this.SaveViewState();
     V_1 = null;
     V_2 = null;
    
     //获取SubControls的SaveViewStateRecursive()结果
     if(this._controls != null)
     {
         V_3 = this._controls.Count;
         for(V_4=0; V_4<V_3; V_4++)
         {
              V_5 = this._controls[V_4];
              V_6 = V_5.SaveViewStateRecursive();
 
              if(V_6 != null)
              {
                   if(V_1 == null)
                   {
                       V_1 = new System.Collections.ArrayList();
                       V_2 = new System.Collections.ArrayList();
                       V_1.Add(V_4);
                       V_2.Add(V_6);
                   }
              }
         }
     }
     V_7 = null;
     //如果Control本身的SaveViewState()结果不为null,
     //而且SuControls的SaveViewStateRecursive()结果不为null
     if((V_0 != null) || (V_1 != null) )
     {
         V_7 = new System.Web.UI.Triplet(V_0, V_1, V_2);
     }
      return(V_7);
}
RenderControl方法
ProcessRequestMain()执行了PreRenderRecursive后,接着就执行RenderControl方法。RenderControl是在Control中定义的方法。RenderControl是执行Control的Render方法,输出html文本。
public void  RenderControl(System.Web.UI.HtmlTextWriter writer) //ok
{
     System.Web.HttpContext V_0;
     int V_1;
     int V_2;
     // flags[16]表示Visible,true时,Visible为false,值为false时,Visible为true
     if(!(this.flags[16]))
     {
         //如果this._page不为null,……
         V_0 = (this._page == null) ? null: this._page._context;
         if(V_0 != null)
         {
              if(V_0.TraceIsEnabled)
              {
                   V_1 =V_0.Response.GetBufferedLength();
                   //Render方法在这里被执行
                   this.Render(writer);
                   V_2 = V_0.Response.GetBufferedLength();
                   V_0.Trace.AddControlSize(this.UniqueID, V_2 - V_1);
              }
         }
         else
         {   //Render方法在这里被执行
              this.Render(writer);
         }
     }
}

Render和RenderChildren
Render和RenderChildren都可以被重载。在System.Web.UI.WebControls命名空间里,大部分Control都是从System.Web.UI.WebControls.WebControl中派生的,WebControl重载Render方法,分拆成三个方法,RenderBeginTage、RenderContent和RenderEndTag。如果Cotnrol是从WebControl中派生,通常只需要重载RenderContent。
 
下面是Control中的代码:
protected virtual void Render(System.Web.UI.HtmlTextWriter writer)
{    this.RenderChildren(writer); }
 
protected virtual void RenderChildren(System.Web.UI.HtmlTextWriter writer) //ok
{   int V_0;
     int V_1;
     if(this._renderMethod != null)
     {   this._renderMethod(writer,this);     }
     else
     {
         if(this._controls != null)
          {
              V_0 = this._controls.Count;
              for(V_1=0; V_1<V_0; V_1 ++)
              {
                   this._controls[V_1].RenderControl(writer);
              }
///
在以前写个一篇关于ASP.NET页面生命周期的草稿,最近又看了看ASP.NET,做个补充,看看页面初始过程到底是怎么样的
下面是ASP.NET页面初始的过程:
1. Page_Init();
2. Load ViewState;
3. Load Postback data;
4. Page_Load();
5. Handle control events;
6. Page_PreRender();
7. Page_Render();
8. Unload event;
9. Dispose method called;
下面对其中的一些过程作下描述:
1. Page_Init();
这个过程主要是初始化控件,每次页面载入执行这个初始过程,包括第一次和以后的Postback(这里说下Postback,其实就可以简单理解成用户点击SUBMIT按钮之类的,把表单<Form>提交给服务器,这就是一次postback),在这里面可以访问控件,但是这里面的控件值不是我们期待的控件里面的值,他只是一个控件的初始值(默认值),举例: 比如一个TextBox1,我们填入了"哈哈",在点击SUBMIT提交了页面后,在Page_Init()里面,我们访问到的TextBox1.Text不是我们的"哈哈",而是开始的""空字符串,如果TextBox1在我们设计的时候提供了默认值,这里访问到的也就是提供的默认值,为什么呢,这就要看下一个过程了.
对应的事件Page.Init
2. Load ViewState
这个过程是载入VIEWSTATE和Postback数据,比如我们上面的TextBox1,这时就赋了"哈哈",所以,在Post_Init()对控件赋值是无意义的,它都会在这个过程里被改写,当然第一次页面载入例外,因为没有VIEWSTATE数据。
没有对应的事件
3.Load Postback data;
上面说了,Postback可以理解成用户提交表单数据,所以这里就是处理表单数据,当然这里要设计到控件的设计,一般情况不会要我们自己处理这个过程,我们暂且略过. (在以前那篇关于ASP.NET页面生命周期的简单描述中,把这个过程和Load ViewState放在了一起,其实那是微软提供的生命周期过程,这里单独提出来是为了让大家明白这是一个单独的过程)
 
没有对应的事件
4. Page_Load();
这个过程也是每次页面载入时一定会执行的,但是注意和Page_Init的区别,上面已经涉及了,这里注意的是一般都会用到Page.IsPostBack,该值指示该页是否正为响应客户端回发而加载,或者它是否正被首次加载和访问。
private void Page_Load(object sender, System.EventArgs e)
{
  if(!Page.IsPostBack)
  {    //第一次执行的CODE HERE  }
  else
  {    //用户提交FORM(即Postback)CODE HERE  }

  //每次这里的都回执行CODE HERE
}
对应的事件Page.Load
5. Handle control events;
这个过程里,相应具体的控件事件,比如private void ListBox1_SelectedIndexChanged(object sender, System.EventArgs e)事件等等
没有对应的事件(我们自己的事件函数都包括在这个过程里比如上面的ListBox1_SelectedIndexChanged)
6. Page_
预先呈递对象,这里是在向用户程序呈现数据的倒数第二步,我估计提供这个过程的意义,也就是在这里能对控件属性等等要呈现给用户的数据进行修改,这也是最后的修改,以前的修改(比如在Page_Init里面)都可能被覆盖.做完这了还会进行一个操作就是保存状态,即SaveViewState.
对应的事件时Page.PreRender
7. Page_Render();
大家可以在浏缆器里View->Source查看到,每个页面都有一个隐藏的<input>,这里面的"__VIEWSTATE"就是我们服务器写回来的页面状态信息,在这个之前,服务器要呈现页面(也就是构造HTML格式的文件),就是从这个"__VIEWSTATE"里面获取的数据,当然大家也注意到了,这里有个Page.Render事件,我们可以添加自己的处理代码,也就是说我们又可以更改数据,不过这里推荐不要在这里修改,既然提供了PreRender,就应该在里面做最后的修改,当然这不是必须的,只是推荐!
对应的事件Page.Render
8. Unload event;
大家应该明白,当想服务器请求一个对象的时候,就会在内存里生成一个继承页面对象,也就是页面的类,它继承自System.Web.UI.Page.
当页面对象从内存中卸载时发生,将触发该事件.
对应的事件Page.Unload
9. Dispose method called;
销毁所有的对象.当从内存释放Page时发生,这是生存期的最后阶段。可能第8和9似乎有些模糊,不过我也没怎么搞清楚,待研究!
对应的事件Dispose
以上就是ASP.NET页面周期的描述。
注意上面灰色背景的文字,如果一个过程中有对应的事件,我们可以自己定义一个函数(当然先在MSDN中找到函数原型),然后在
InitializeComponent中向事件的链表上添加上去,像下面:
private void InitializeComponent()
{   
  this.Unload += new System.EventHandler(this.MainWebForm_Unload);
  this.Load += new System.EventHandler(this.Page_Load);
  this.Init += new System.EventHandler(this.Page_Init);
  this.PreRender += new System.EventHandler(this.My_PreRender);
}
对于几个没有对应事件的过程,比如2.Load ViewState,我们可以重载Page的虚函数protected override void LoadViewState(object savedState);来添加自己的控制代码,不过切忌掉用基类的对应方法,比如:
protected override void LoadViewState(object savedState)
{
  //自己处理VIEWSTATE
  base.LoadViewState (savedState);
}


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值