深入研究Asp.net页面的生命周期

 

深入研究Asp.net页面的生命周期
asp.net 页面从初始化到卸载事件顺序

客户发出POST请求-〉创建Page派生类,调用构造函数-〉调用Page类的IHttpHandler.ProcessRequest方法-〉激活Page类的Init事件-〉调用Page类的CreateChildControls虚方法-〉从POST变量和VIEWSTATE中还原服务器端控件状态-〉激活Page类的Load事件-〉激活服务器端控件事件-〉激活Page类的PreRender事件-〉调用Page类的Render虚方法-〉调用Page类的RenderChildren虚方法-〉发送HTTP响应给客户端-〉激活Page类的Unload事件-〉丢弃Page派生类的实例。


介绍
Asp.net是微软.Net战略的一个组成部分。它相对以前的Asp有了很大的发展,引入了许多的新机制。本文就Asp.net页面的生命周期向大家做一个初步的介绍,以期能起到指导大家更好、更灵活地操纵Asp.net的作用。
当一个获取网页的请求(可能是通过用户提交完成的,也可能是通过超链接完成的)被发送到Web服务器后,这个页面就会接着运行从创建到处理完成的一系列事件。在我们试图建立Asp.net页面的时候,这个执行周期是不必去考虑的,那样只会自讨苦吃。然而,如果被正确的操纵,一个页面的执行周期将是一道有效而且功能强大的工具。许多开发者在编写Asp.net的页面以及用户控件的时候发现,如果知道整个过程中发生了什么以及在什么时候发生将对完成整个任务起到很重要的帮助作用。下面我就向大家介绍一下一个Asp.net页面从创建到处理完成过程中的十个事件。同时,也向大家展示如何在这些事件中添加自己的代码以达到预定的效果。
一.初始化对象
一个页面的控件(以及页面本身)最初应被正确的初始化。通过在你的C#文件的构造函数中声名所有对象(如图1),页面就知道要创建多少对象以及它们的类型。一旦你在你的构造函数中声名了所有的对象,你就可以通过继承类、方法、事件或是属性访问它们。然而,如果你的一些对象是在Aspx文件中指定的一些控件,那么这些控件就没有属性可言了。同时,通过代码访问它们会产生一些意外的错误,因为这些控件实例是没有一个确定的创建顺序的(如果它们是被一起创建的)。还有,你可以通过OnInit来重载初始化事件,图示如下(图1):
 
 
图1
 
二.导入Viewstate数据
在初始化事件后,所有控件只可以通过它们的ID被引用访问(因为还没有相应的DOM可使用)。在LoadViewState这个事件中,所有的控件将获得它们的第一个属性:Viewstate属性。这个属性最终将被返回给服务器以判断这个页面是已经被用户访问完毕还是仍然在被用户所访问。Viewstate属性以“名称/值”对的字符串方式被保存,它包含了控件的文本以及值等信息。该属性被存储在一个隐藏的<input>控件的值属性里,在请求页面时被传递。这种方式比起Asp3.0的维持、判断页面状态的方式有了很大的进步啊。还有,你可以重载LoadViewState事件函数来对相应的控件进行值设定。下图(图2)是一个例子:
 
 
图2
 
三.用LoadPostData处理Postback数据
在页面创建的这个阶段,服务器对页面上的控件提交的表单数据(在Asp.net中称postback数据)进行处理。当一个页面提交一个表单时,框架就在每个提交了数据的控件上执行一个IPostBackDataHandler接口操作。然后页面执行LoadPostData事件,解析页面,找到每个执行了IpostBackDataHandler接口操作的控件,并用恰当的postback数据更新这些控件状态。Asp.net是通过用NameValue集中的“名称/值”对和每个控件的唯一的ID匹配来实现这一操作的。所以,在Asp.net的页面上每个控件必须有一个唯一的ID,不可以出现几个控件共有ID的情况。即使是用户自定义的一些控件,框架也会赋予它们各自唯一的ID的。在LoadPostData事件后,就要执行下面的RaisePostDataChanged事件了。
四.导入对象
在Load事件中,对象都实例化了。所有的对象第一次被布置在DOM页面(在Asp.net中称控件树)里了并且可以通过代码或是相关的位置被引用。这样,对象就可以很容易的从客户端获得诸如宽度、高度、值、可见性等在Html中的属性值。在Load事件中,当然还有像设置控件属性等操作的发生。这个过程是整个生命周期中最重要、最主要的,你可以通过调用OnLoad来重载Load事件,图示如下(图3):
 
 
图3
 
五.RaisePostBackChanged事件
就像在上面提到的那样,这个事件是发生在所有的控件执行了IPostBackDataHandler接口操作并被正确的postback数据更新后的。在这个过程中,每个控件都被赋予一个布尔值来标志该控件有没有被更新。然后,Asp.net就在整个页面上寻找任何已被更新过的控件并执行RaisePostDataChanged事件操作。不过,这个事件是要在所有的控件都被更新了以及Load事件完成后才进行的。这样就保证了一个控件在被postback数据更新前,别的控件在RaisePostDataChanged事件中是不会被手动改变的。
六.处理客户端PostBack事件
当由postback数据在服务器端引起的事件都完成后,产生postback数据的对象就执行RaisePostBackEvent事件操作。可是会有这种情况,由于一个控件状态的改变使得它将表单返回给服务器或是用户点击了提交按钮使得表单返回给服务器。在这种情况下应该有相应的处理代码来体现事件驱动这一面向对象(OOP)编程原则。由于要满足呈现给浏览器的数据的精确性要求,在一系列postback事件中RaisePostBackEvent事件是最后发生的。
在postback过程中改变的控件不应在执行功能函数被调用后更新。也就是说,任何由于一个预期的事件而改变的数据应该在最终的页面上被反映出来。你可以通过修改RaisePostBackEvent函数来满足你的要求,图示如下(图4):
 
 
图4
 
七.预先呈递对象
可以改变对象并将改变保存的最后时刻就是这一步――预先呈递对象。这样,你可以在这一步对控件的属性、控件树结构等作出最后的修改。同时还不用考虑Asp.net对其作出任何改变,因为此时已经脱离了数据库调用以及viewstate更新了。在这一步之后,对对象的所有修改将最终被确定,不能被保存到页面的viewstate中了。你可以通过OnPreRender来重载这一步。
八.保存ViewState
所有对页面控件的修改完成后viewstate就被保存了。对像的状态数据还是保留在隐藏的<input>控件里面,呈现给Html的对象状态数据也是从这里取得的。在SaveViewState事件中,其值能被保存到viewstate对象,然而这时在页面上控件的修改却不能了。你可以用SaveViewState来重载这一步,图示如下(图5):
 
 
图5
 
九.呈递给Html
运用Html创建给浏览器输出的页面的时候Render事件就发生了。在Render事件过程中,页面调用其中的对象将它们呈递给Html。然后,页面就可以以Html的形式被用户的浏览器访问了。当Render事件被重载时,开发者可以编写自定义的Html代码使得原先生成的Html都无效而按照新的Html来组织页面。Render方法将一个HtmlTextWriter对象作为参数并用它将Html在浏览器上以网页的形式显示。这时仍然可以做一些修改动作,不过它们只是客户端的一些变化而已了。你可以重载Render事件,图示如下(图6):
 
 
图6
 
十.销毁对象
在呈递给Html完成后,所有的对象都应被销毁。在Dispose事件中,你应该销毁所有在建立这个页面时创建的对象。这时,所有的处理已经完毕,所以销毁任何剩下的对象都是不会产生错误的,包括页面对象。你可以重载Dispose事件,见图6。
全文总结
以上就是Asp.net页面生命周期中的十个事件。每次我们请求一个Asp.net页面时,我们都经历着同样的过程:从初始化对象到销毁对象。通过了解Asp.net页面的内部运行机制,我相信大家在编写、调试代码的时候会更加游刃有余的。

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/wangjh422/archive/2008/03/06/2153813.aspx

 

 

 

 

代码
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;

public partial class _Default : Page 
{
    
protected void Page_Load(object sender, EventArgs e)
    {

    }

    
#region OnPreInit 第一步
    
protected override void OnPreInit(EventArgs e)
    {
        
//检查 IsPostBack 属性来确定是不是第一次处理该页。

        
//创建或重新创建动态控件。

        
//动态设置主控页。

        
//动态设置 Theme 属性。

        
//读取或设置配置文件属性值。

        
//注意  
        
//如果请求是回发请求,则控件的值尚未从视图状态还原。如果在此阶段设置控件属性,则其值可能会在下一事件中被重写。
 

        
base.OnPreInit(e);
    }
    
#endregion

    
#region OnInit 第二步
    
protected override void OnInit(EventArgs e)
    {
        
//在所有控件都已初始化且已应用所有外观设置后引发。使用该事件来读取或初始化控件属性。
        base.OnInit(e);
    }
    
#endregion

    
#region OnInitComplete 第三步
    
protected override void OnInitComplete(EventArgs e)
    {
        
//由 Page 对象引发。使用该事件来处理要求先完成所有初始化工作的任务。

        
base.OnInitComplete(e);
    }
    
#endregion

    
#region PreLoad 第四步
    
protected override void OnPreLoad(EventArgs e)
    {
        
//如果需要在 Load 事件之前对页或控件执行处理,请使用该事件。 

        
//在 Page 引发该事件后,它会为自身和所有控件加载视图状态,然后会处理 Request 实例包括的任何回发数据。

        
base.OnPreLoad(e);
    }
    
#endregion 

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

        
base.OnLoad(e);
    }
    
#endregion

    
#region 控件事件 第六步
    
protected void Button1_Click(object sender, EventArgs e)
    {
        
//用这些事件来处理特定控件事件,如 Button 控件的 Click 事件或 TextBox 控件的 TextChanged 事件。

        
//注意  
        
//在回发请求中,如果页包含验证程序控件,请在执行任何处理之前检查 Page 和各个验证控件的 IsValid 属性。
 

    }
    
#endregion

    
#region OnLoadComplete 第七步
    
protected override void OnLoadComplete(EventArgs e)
    {
        
//对需要加载页上的所有其他控件的任务使用该事件。

        
base.OnLoadComplete(e);
    }
    
#endregion

    
#region OnPreRender 第八步
    
protected override void OnPreRender(EventArgs e)
    {
        
//在该事件发生前:

        
//Page 对象会针对每个控件和页调用 EnsureChildControls。 

        
//设置了 DataSourceID 属性的每个数据绑定控件会调用 DataBind 方法。有关更多信息,请参见下面的数据绑定控件的数据绑定事件。

        
//页上的每个控件都会发生 PreRender 事件。使用该事件对页或其控件的内容进行最后更改。

        
base.OnPreRender(e);
    }
    
#endregion 

    
#region SaveStateComplete 第九步
    
protected override void OnSaveStateComplete(EventArgs e)
    {
        
//在该事件发生前,已针对页和所有控件保存了 ViewState。将忽略此时对页或控件进行的任何更改。

        
//使用该事件执行满足以下条件的任务:要求已经保存了视图状态,但未对控件进行任何更改。

        
base.OnSaveStateComplete(e);
    }
    
#endregion

    
#region Render 第十步
    
//Render
    
//这不是事件;在处理的这个阶段,Page 对象会在每个控件上调用此方法。所有 ASP.NET Web 服务器控件都有一个用于写出发送给浏览器的控件标记的 Render 方法。

    
//如果创建自定义控件,通常要重写此方法以输出控件的标记。不过,如果自定义控件只合并标准的 ASP.NET Web 服务器控件,不合并自定义标记,则不需要重写 Render 方法。有关更多信息,请参见开发自定义 ASP.NET 服务器控件。

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

    
#endregion

    
#region OnUnload 第十一步       
    
protected override void OnUnload(EventArgs e)
    {
        
//该事件首先针对每个控件发生,继而针对该页发生。在控件中,使用该事件对特定控件执行最后清理,如关闭控件特定数据库连接。

        
//对于页自身,使用该事件来执行最后清理工作,如:关闭打开的文件和数据库连接,或完成日志记录或其他请求特定任务。

        
//注意  
        
//在卸载阶段,页及其控件已被呈现,因此无法对响应流做进一步更改。如果尝试调用方法(如 Response.Write 方法),则该页将引发异常。
 

        
base.OnUnload(e);
    }
    
#endregion
}

 

 

 

 

 

 

 

 

有很多理由去解释理解ASP.NET页面生命周期是非常重要的,主要是要去理解什么地方放置什么特定的方法,什么时候我们应该设置什么相关的属性。如果去开发自定义的服务器控件,理解生命周期对纠正控件初始化时候的错误,以及使用view-state和后台代码设置属性是非常有用的。(控件事件只与ASP.NET页面相关)

        页面生命周期要看它是否是第一次请求,还是回发(本身页面请求),最后决定是否到Web服务器。当一个网页被Web服务器请求时,在回发到web浏览器之前,会经过一系列步骤/事件(如初始化,控件实例化,state的恢复和保存,执行事件处理代码,渲染)。

        如果我们正确地使用和操作页面生命周期事件,它对web应用程序开发会是一个非常方便和强大的工具。

       IIS : 它是Microsoft .NET默认的web服务器。IIS帮助你发布web sites和web application。当IIS Web服务器接受一个Web资源(文件)的请求,它检查文件的扩展名(例如:.aspx, ascx, .ashx 和.asmx),然后决定使用哪个 ISAPI 扩展来处理这个请求,然后传递这个请求给正确的 ISAPI扩展。

       ASPNET_ISAPI.DLL: IIS加载这个dll,然后将页面请求发送给这个dll。这个dll为进一步处理而加载HTTPRuntime 。
       ASPNET_WP.EXE:它包含了应用程序池。每个应用程序池可以包含任意数量的应用程序。应用程序池也称为应用程序域。当一个网页被请求时,查找IIS的当前的应用程序正在运行的应用程序池,并且将请求转发到各自的工作进程。
       通常浏览器每次发送请求的时候,页面的生命周期每次都会执行一遍,页面的实例被创建、HTTP 运行时调用ProcessRequest 、然后执行起始页。
      下面的表格显示了每个阶段和相应的事件:
阶段 事件/方法  
页面初始化   Page_Init 
加载View State    LoadViewState  
回发数据处理 LoadPostData  
页面加载  Page_Load  
回发通知   RaisePostDataChangedEvent 
回发事件处理 RaisePostBackEvent 
页面预渲染 Page_PreRender  
保存viewstate SaveViewState  
Page渲染   Page_Render  
Page 卸载 Page_UnLoad  
 
      Asp.net确定对一个用户发起的页面请求是被解析和编译还是发送提供的网页缓存。在生命周期之前,变化非常多。在这之后,检查这个请求是否是正常的请求,在此之后,还要求检查的是一个正常的要求,回发,跨页回发或回调。
     该页面构造函数创建的控件树,在HTTP运行时实例化页面类来处理请求。
PreInit
这一事件是在页面生命周期的开始。每个页面控件初始化和根据aspx代码设置属性。
   可以改变或设置母版页或者主题
   创建或重新创建动态控件。
   读取或设置配置属性值。
Init
首先, Page的Init事件发生,然后页面上每个控件Init事件发生。在此阶段,Viewstate 信息是不可用的。
   控件已经被初始化
   如果有的话,主题皮肤被应用。
   初始化控件属性
InitComplete
    此事件是用来处理所有要求初始化都完成的任务。
PreLoad
    此事件发生在执行任何处理之前,在Load之前发生。使用这个事件,如果您在Load事件之前,需要在网页或者控件上执行相关的处理,页面实例引发此事件之前,它为页面和所有的控件加载view state 。然后处理回发数据的任何附带要求的实例。
Load
   在控件中设置属性, 建立数据库的连接
Control Events
   有一些控件的特定的事件,例如: Button Click, DropDownIndexChanged 等等。
Load Complete
   这些事件用来处理一些在Load事件处理完成之后需要处理的任务。
PreRender
   在这种事件中,页面可以确保所有子控件都被创建。页面为所有的子控件调用EnsureChildControls也包括自己。每一个控件的数据源/绑定属性设置为它的绑定方法调用。
SaveStateComplete
   这个事件发生在页面和所有的控件的编码和保存viewstate之后。
Render
   每一个asp.net控件都有渲染方法,页面的实例调用此方法并输出控件的标记
Unload
   Unload事件用来执行清除任务,像关闭数据库连接,关闭打开的文件,完成日志,以及一些其他的任务。该事件首先针对每个控件发生,继而针对该页发生。

 

 

 

 

posted @ 2010-04-11 22:47  KiNg.JiOnG  阅读(564)  评论(0编辑  收藏  举报
查看博客访问人数(点击):