ASP.NET中页和控件的生命周期与事件
1、预初始化
  预初始化对应Page的PreInit事件和OnPreInit方法。Page_PreInit发生在页面初始化之前,页面上的控件还尚未创建。此时可以:1)创建或重新创建动态控件;2)动态设置主控页。
  具体的重载代码如下,并且加注了本步骤Page所执行工作和可处理的任务(后同)
#region OnPreInit 第一步 protected override void OnPreInit(EventArgs e) { //检查 IsPostBack 属性来确定是不是第一次处理该页。 //创建或重新创建动态控件。 //动态设置主控页。 //动态设置 Theme 属性。 //读取或设置配置文件属性值。 //注意!如果请求是回发请求,则控件的值尚未从视图状态还原。如果在此阶段设置控件属性,则其值可能会在下一事件中被重写。 base.OnPreInit(e); } #endregion
  2、初始化
  初始化对应Page的Init事件和OnInit方法。此时将执行页面初始化,实例化页面上的控件对象,可以读取或初始化控件属性。
#region OnInit 第二步 protected override void OnInit(EventArgs e) { //在所有控件都已初始化且已应用所有外观设置后引发。使用该事件来读取或初始化控件属性。 base.OnInit(e); } #endregion
 3、初始化完成
  初始化完成对应Page的InitComplete事件和OnInitComplete方法。由 Page 对象触发,使用该事件来处理要求预先完成所有初始化工作的任务。
#region OnInitComplete 第三步 protected override void OnInitComplete(EventArgs e) { //由 Page 对象引发。使用该事件来处理要求先完成所有初始化工作的任务。 base.OnInitComplete(e); } #endregion
4、预加载
  预加载对应Page的PreLoad事件和OnPreLoad方法。使用该事件在 Load 事件之前对页或控件执行处理。
#region PreLoad 第四步 protected override void OnPreLoad(EventArgs e) { //如果需要在 Load 事件之前对页或控件执行处理,请使用该事件。 //在 Page 引发该事件后,它会为自身和所有控件加载视图状态,然后会处理 Request 实例包括的任何回发数据。 base.OnPreLoad(e); } #endregion
  读者可能会注意到上面代码的注释中有如下内容“在 Page 引发该事件后,它会为自身和所有控件加载视图状态,然后会处理 Request 实例包括的任何回发数据。”。不需要过多解释,在OnPreLoad方法的基类中最终会引发如下两个步骤:
  5、 加载视图状态
  这是个比较重要的方法,我们知道,对于每次请求,实际上是由不同的页面类实例来处理的,为了保证两次请求间的状态,ASP.Net使用了ViewState。LoadViewState方法就是从ViewState中获取上一次的状态,并依照页面的控件树的结构,用递归来遍历整个树,将对应的状态恢复到每一个控件上。
  6、 处理回发数据
  这个方法是用来检查客户端发回的控件数据的状态是否发生了改变。方法的原型:
public virtual bool LoadPostData(string postDataKey, NameValueCollection postCollection)
  postDataKey是标识控件的关键字(也就是postCollection中的Key),postCollection是包含回发数据的集合,我们可以重写这个方法,然后检查回发的数据是否发生了变化,如果是则返回一个True。摘自MSDN 的原文如是:“如果控件状态因回发而更改,则 LoadPostData 返回 true;否则返回 false。页框架跟踪所有返回 true 的控件并在这些控件上调用RaisePostDataChangedEvent。”(这个方法是System.Web.WebControls.Control中定义的,也是所有需要处理事件的自定义控件需要处理的方法,在这里我们不详细展开,读者可自行查阅MSDN。)
  7、 加载
  加载对应Load事件和OnLoad方法,此时页面上的控件都已生成完毕,可以处理如下任务:1)使用IsPostBack 属性确定是否是第一次处理该页;2)读取和更新控件属性;3)在控件上绑定动态的数据。Page_Load方法响应了Load事件,这个事件是在System.Web.WebControl.Control类中定义的(这个类是Page和所有服务器控件的鼻祖),并且在OnLoad方法中被触发。
    对于这个事件,相信大多数读者都会比较熟悉,用VS.Net生成的页面中的Page_Load方法就是响应Load事件的方法,对于每一次请求,Load事件都会触发,Page_Load方法也就会执行,相信这也是大多数开发者了解ASP.Net的第一步。
  很多人可能碰到过这样的事情,写了一个PageBase类,然后在Page_Load中来验证用户信息,结果发现不管验证是否成功,子类页面的Page_Load总是会先执行,这个时候很可能留下一些安全性的隐患,用户可能在没有得到验证的情况下就执行了子类中的Page_Load方法。
  出现这个问题的原因很简单,因为Page_Load方法是在OnInit中被添加到Load事件中的,而子类的OnInit方法中是先添加了Load事件,然后再调用base.OnInit,这样就造成了子类的Page_Load被先执行了。
  要解决这个问题也很简单,有两种方法:
  1) 在PageBase中重载OnLoad方法,然后在OnLoad中验证用户,然后调用base.OnLoad,因为Load事件是在OnLoad中触发,这样我们就可以保证在触发Load事件之前验证用户。
  2) 在子类的OnInit方法中先调用base.OnInit,这样来保证父类先执行Page_Load。
重载代码如下:
#region OnLoad 第七步
protected override void OnLoad(EventArgs e)
{
//Page 在 Page 上调用 OnLoad 事件方法,然后以递归方式对每个子控件执行相同操作,如此循环往复,直到加载完本页和所有控件为止。
//使用 OnLoad 事件方法来设置控件中的属性并建立数据库连接。
base.OnLoad(e);
}
#endregion
  8、 发送回发更改通知
  这个方法对应第6步的处理回发数据,如果处理回发数据返回True,页面框架就会调用此方法来触发数据更改的事件,所以自定义控件的回发数据更改事件需要在此方法中触发。同样这个方法对于Page来说没有太大的用处,当然你也可以在Page的基础上自己定义数据更改的事件。
  9、 处理回发事件
  由用户操纵页面引发,具体事件和引发的次数、顺序依具体页面和用户的操作而定。这个方法是大多数服务器控件事件引发的地方,当请求中包含控件事件触发的信息时,页面控件会调用相应控件的RaisePostBackEvent方法来引发服务器端的事件。
代码示例如下:
#region 控件事件 第九步
protected void Button1_Click(object sender, EventArgs e)
{
//用这些事件来处理特定控件事件,如 Button 控件的 Click 事件或 TextBox 控件的 TextChanged 事件。
//注意!在回发请求中,如果页包含验证程序控件,请在执行任何处理之前检查 Page 和各个验证控件的 IsValid 属性。
}
#endregion
  这里又引出另一个常见的问题:经常有初级开发者疑惑,为什么修改提交后的数据并没有更新?
  多数的情况都是他们没有理解服务器事件的触发流程,我们可以看出,触发服务器事件是在Page的Load之后,也就是说页面会先执行Page_Load,然后才会执行按钮(这里以按钮为例)的点击事件,很多朋友都是在Page_Load中绑定数据,然后在按钮事件中处理更改,这样做有一个毛病,Page_Load永远都是在按钮事件之前执行,那么意味着数据还没来得及更改,Page_Load中的数据绑定的代码就先执行了,原有的数据又赋给了控件,那么执行按钮事件的时候,实际上获得的是原有的数据,那么更新当然就没有效果了。
  更改这个问题也非常简单,比较合理的做法是把数据绑定的代码写成一个方法,我们假设为BindData:
private void BindData()
{
//绑定数据
}
  然后修改PageLoad:
private void Page_Load( object sender,EventArgs e )
{
if( !IsPostBack )
{
BindData(); //在页面第一次访问的时候绑定数据
}
}
  最后在按钮事件中:
private Button1_Click( object sender,EventArgs e )
{
//更新数据
BindData();//重新绑定数据
}
  10、加载完成
  加载完成对应Page的LoadComplete事件和OnLoadComplete方法。由 Page 对象触发,使用该事件来处理需要加载页上的所有其他控件的任务。
#region OnLoadComplete 第十步
protected override void OnLoadComplete(EventArgs e)
{
//对需要加载页上的所有其他控件的任务使用该事件。
base.OnLoadComplete(e);
}
#endregion
  11、预呈现
  预呈现对应Page的PreRender事件和OnPreRender方法。最终请求的处理都会转变为发回服务器的响应,预呈现这个阶段就是执行在最终呈现之前所作的状态的更改,因为在呈现一个控件之前,我们必须根据它的属性来产生Html,比如Style属性,这是最典型的例子。在预呈现之前,我们可以更改一个控件的Style,当执行预呈现的时候,我们就可以把Style保存下来,作为呈现阶段显示Html的样式信息。简要地讲:HTML页面生成完毕,接下来就要发送给客户端了,此时可以在HTML页面上追加一些附加信息。对页的内容进行最后更改。
#region OnPreRender 第十一步
protected override void OnPreRender(EventArgs e)
{
//在该事件发生前Page 对象会针对每个控件和页调用EnsureChildControls。
//设置了 DataSourceID 属性的每个数据绑定控件会调用 DataBind 方法。有关更多信息,请参见下面的数据绑定控件的数据绑定事件。
//页上的每个控件都会发生 PreRender 事件。使用该事件对页或其控件的内容进行最后更改。
base.OnPreRender(e);
}
#endregion
  12、保存状态
  这个阶段是针对加载状态的。我们多次提到请求之间是不同的实例在处理,所以我们需要把本次的页面和控件的状态保存起来,这个阶段就是把状态写入ViewState的阶段。
#region SaveStateComplete 第十二步
protected override void OnSaveStateComplete(EventArgs e)
{
//在该事件发生前,已针对页和所有控件保存了 ViewState。将忽略此时对页或控件进行的任何更改。
//使用该事件执行满足以下条件的任务:要求已经保存了视图状态,但未对控件进行任何更改。
base.OnSaveStateComplete(e);
}
#endregion
  13、呈现
  到这里实际上页面对请求的处理基本就告一段落了,在Render方法中会递归整个页面的控件树,依次调用Render方法,把对应的Html代码写入最终响应的流中。
#region Render 第十三步
//Render
//这不是事件;在处理的这个阶段,Page 对象会在每个控件上调用此方法。所有 ASP.NET Web 服务器控件都有一个用于写出发送给浏览器的控件标记的 Render 方法。
//如果创建自定义控件,通常要重写此方法以输出控件的标记。不过,如果自定义控件只合并标准的 ASP.NET Web 服务器控件,不合并自定义标记,则不需要重写 Render 方法。有关更多信息,请参见开发自定义 ASP.NET 服务器控件。
//用户控件(.ascx 文件)自动合并呈现,因此不需要在代码中显式呈现该控件。
#endregion
  14、处置
  实际上就是Dispose方法,在这个阶段会释放占用的资源,例如数据库连接。
  15、卸载
  卸载对应Page的Unload事件和OnUnload方法。执行最后的清理工作,一般我们无需干涉。最后页面会执行OnUnLoad方法触发UnLoad事件,处理在页面对象被销毁之前的最后任务。实际上ASP.Net提供这个事件只是设计上的考虑,通常资源的释放都会在Dispose方法中完成,所以这个方法也变成鸡肋了。
#region OnUnload 第十五步
protected override void OnUnload(EventArgs e)
{
//该事件首先针对每个控件发生,继而针对该页发生。在控件中,使用该事件对特定控件执行最后清理,如关闭控件特定数据库连接。
//对于页自身,使用该事件来执行最后清理工作,如:关闭打开的文件和数据库连接,或完成日志记录或其他请求特定任务。
//注意!在卸载阶段,页及其控件已被呈现,因此无法对响应流做进一步更改。如果尝试调用方法(如 Response.Write 方法),则该页将引发异常。
base.OnUnload(e);
}
#endregion
没有目标的人都只在帮有目标的人完成目标


 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号