ASP.NET页面生命周期

来源:互联网 发布:名不虚传软件下载 编辑:程序博客网 时间:2024/06/05 15:14

声明:此文转载自http://www.lxzhu.net/blogcn/tags/life-cycle(太初有道)。

在Asp.NET WEB FORM的开发中,页面的生命周期以及生命周期中的主要事件是无法忽略的。页面生命周期的详细解释可以参考MSDN – ASP.NET Page Life Cycle Overview 。这篇文章比较长。下面的内容是我阅读这篇文章之后的摘要。主要都是开发过程中容易弄错的地方。

生命周期

很多人(包括我)在提到页面的生命周期的时候,都是指页面生命周期里面所发生的事件(Event). 其实,那不是生命周期本身,而是页面生命周期给程序员提供的扩展点。Asp.NET框架在生命周期过程中发出各种事件来让我们参与生命周期。所以事件不是生命周期。

Asp.NET页面生命周期包括下面的几个阶段

阶段说明请求页面(Page Request)严格地说,这个阶段不是页面生命周期的一部分。而是页面生命周期之前的一个阶段。Asp.NET接收到一个页面请求之后,执行分析和编译页面的工作或者从缓存中取出该页面发送给客户端。开始(Start)在这个阶段,Asp.NET设置Page对象的Request和Response属性;确定该页面请求是否为PostBack;给页面设置UICulture属性。初始化(Initialization)页面和控件对象的初始化;为控件设置UniqueID;为页面设置Master Page和Theme;需要特别注意的是,在这个阶段,客户端提交的数据,还没有被恢复到页面控件中去,包括ViewState在内。加载(Load)加载ViewState; 将表单数据设置到对应控件中去。回发事件(PostBack event)如果当前请求是一个PostBack请求,那么必然有某个事情导致了这次PostBack, 现在就到了执行该事件在服务器上的Event Handler的时候了。         
这个阶段主要的问题在于Validation和Event Handler的执行次序。          
    1.如果该Event本身没有直接导致Validation, 比如我在Button上设置了不需要Validation,那么该Button的Click Event Handler先执行,然后页面上会执行页面上所有Validator 控件的Validator方法,然后设置页面的IsValid属性。          
    2.如果该Event本身直接导致了Validation,比如用户点击了一个Button,这个Button默认就会导致Validator被调用。这时候,Validation的执行是在Button的Click Event Handler之前的。等你进入Button的Click Event Handler的时候,页面的IsValid属性已经设置好了。渲染(Rendering)Asp.NET将页面和所有控件的ViewState保存到Form表单中去。然后调用所有控件和页面的Render方法来输出Html.卸载(Unload)执行到这个阶段的时候,页面已经将Html输出到客户端了。这个阶段要做的事情就包括:卸载Request对象和Response对象,执行一些清理工作。

生命周期事件

AutoEventWireup

关于生命周期中的事件,首先要解释的是AutoEventWireup. 我第一次看到Page_Load这个方法名字就觉得很神奇,但是从VB过来的程序员觉得很自然。作为一个C#程序员,我没能猜出来Page_Load这个方法是如何被调用的。后来查到了AutoEventWireup这个东西。这个配置项默认是true,可以在@Page指令中设置为false.

Page_Load的方法签名是Asp.NET Web Form保留的特殊方法签名。当页面或者控件的AutoEventWireup设置为true(默认值)的时候,在页面或者控件的Load事件的响应方法OnLoad(这是一个可以override的virtual方法)中会调用这个名字为Page_Load的方法(当然会先检查方法签名)。

如果我们将AutoEventWireup设置为false之后,应该如何参与页面的Load事件呢? 前一段已经提到了,Load事件的实际Event Handler是OnLoad(Page和Control类会自动将自己的OnLoad方法注册为自己的Load事件的handler). 所以你直接override OnLoad方法就好了。

那么在AutoEventWireup设置为true的时候,可不可以也override OnLoad这个方法呢? 是可以的. 但是有一点要注意,如果你同时也写了Page_Load 方法,而且希望它能工作,那么一定要在你的OnLoad方法中调用base.OnLoad, 因为正是在这个base.OnLoad中调用了Page_Load方法。

除了Page_Load方法, 页面上其它事件都有对应的Page_XXX方法。处理方式和Page_Load也都一样。

Bottom Up vs Top Down?

这个问题在日常开发中也比较常见,比如你在一个Page上写了一个Page_Load方法,然后该页面包含的UserControl中也有Page_Load方法。那么这两个Page_Load谁先执行?

答案是Page的Page_Load先执行。 规律是Load事件的执行次序是Top Down的,也就是从容器开始执行,然后是容器里面的子控件按照深度优先递归执行(按照数据结构的说法,就是前序遍历控件树).

那么Page_Init呢? 这回可就是UserControl先执行了。规律是Init事件的执行次序是Bottom Up的,也就是从控件树的叶子节点开始,按照广度优先向上递归(按照数据结构的说法,就是后序遍历控件树).

下面这张表记录了页面中事件/阶段的执行模式:

事件Page ONLYControl ONLYTop DownButton UpPreInitYES   Init   YESInitCompleteYES   LoadViewState  YES ProcessPostData  YES PreLoadYES   Load  YES PostBack Event YES  LoadCompleteYES   PreRender  YES DataBinding Event YES  PreRenderCompleteYES   SaveViewState  YES SaveStateCompleteYES   Render   YESUnload   YES

这张表使用了三种背景颜色:

  • 白底黑字:这个事件只有Page或者Control才有,所以不存在和控件中对应事件的次序问题。
  • 绿底白字:这个事件是Page和Control都有的,需要注意其执行次序。
  • 蓝底黑字:这不是一个事件,它是Page和Control都具有的方法,其执行模式也采用了Top Down和Bottom Up的设计。

我们每天都在处理的XXXChanged和XXXClicked在哪里?  PostBack Event就是,这一类事情统统都称为PostBack Event。它们只发生在控件上。但是Event Handler可能是Page的方法。

我们每天处理的DataBind呢? 你应该已经看到了DataBinding Event了,但是DataBinding有特殊的地方要说。

DataBinding Event

DataBinding在PreRender之后? 注意上面的表格把DataBinding列在了PreRender的后面。为什么这件事这么重要? 我们做UserControl的人(注意不是写ServerControl)的人,经常将PreRender作为UserControl的生命周期的最后一个阶段来处理(有多少写UserControl的程序员敢在Render方法里面写代码?)。但是DataBinding居然在PreRender之后

如果有人把你写的User Control用到了GridView的模板里面,会怎么样呢?

执行PreRender这个阶段的时候,你的用户控件还没有实例化出来,显然不在页面的控件树里面。等你的用户控件在Data Binding过程中被实例化之后,Asp.NET Web Form的PreRender阶段已经过去了,那么你在UserControl里面安装的final fence 直接被微软跳过去了吧。在回头看看上面的表中DataBinding之前的其它事件或者virtual方法,和PreRender一样,都没有机会执行。所以,如果你的UserControl将会被放到支持Data Binding的控件中去,你可得好好研究如何重载Render方法。你此时最好认为自己在写ServerControl 。

DataBinding Event一定在PreRender之后? 不一定的,只有在模板文件(aspx/ascx)中通过DataSourceID给数据控件设置数据源才是这样的。如果你是在代码里面直接调用的DataBind()方法,那么DataBinding Event会立即执行。立即执行是不是就没有问题呢?  我们假设你在某个Button的OnClick里面执行的DataBind方法,那么你可以看看在OnClick是在什么时候执行. OnClick是PostBack Event, 在Load之后,Load不重要吗? 可是Load确实没机会被执行了。所以,即使是立即执行DataBinding,也是会有问题的。

Master Page 呢?

Master Page和页面之间的关系是:Master Page的行为和页面中的控件一致。那么Master Page和页面中其它控件的关系呢? 我们可以认为:

  • Master Page是页面的直接子控件。
  • Master Page永远都是页面的第一个子控件。

InitializeCulture & IsPostBack

InitializeCulture和IsPostBack这两件事对页面和控件中的代码都非常重要。

如果你的代码在InitializeCulture之前执行了,那么你的Culture可能会不正确,这就直接导致资源文件加载不正确,用户就会在页面上看到莫名奇妙的东西。

如果你的代码在IsPostBack属性尚未设置的情况下就开始运行了。那就危险了,因为Asp.NET Web Form输出的Html Form默认会提交给本页。如果IsPostBack不确定, 你就无法确认当前请求是用户第一次请求页面还是用户正在提交已经填写好了的表单。

不过你不用当心,这两件事是在页面的PreInit阶段处理的,而Control没有PreInit的阶段,也就是说这两件事完全由Page来做。那么我们唯一需要注意的就是:如果你Override了PreInit,请一定要先调用base.PreInit方法,从而可以保证,你的自定义代码开始之前,这两件事已经都处理过了。

原创粉丝点击