asp.net 2.0中页的生存周期(Lifecycle)和动态控件

asp.net 2.0中页的生存周期(Lifecycle)和动态控件

作者:laolaowhn 出处:个人博客  2007-9-3 11:51:07 阅读 534  次

  

介绍

弄懂  Page  的生存周期(lifecycle)对于开发  asp.net  应用程序来说是非常重要的。很多.net  初学者在处理动态加载控件的时候都出现过回发 后丢值,丢状态之类的问题。HTTP  协议是无状态的,这就是  web  程序不同与  windows  程序的一个天生的问题,如果要学习  asp.net  的话, Page  的生存周期将是你最重要的基础之一。事件的顺序是怎样的,特别是  asp.net 2.0新增了母板页后,使其变得更复杂了,本文的目的就是通过解释每一个事件的顺序及其用途让你弄清楚这些事件到底是怎么回事。

  

背景

  asp.net  应用程序中,用户总是要去请求一个 .aspx  页的,让我们感兴趣的一件事就是在用户访问一个.aspx  页的时候,应用程序所属的  web  服务器到底做了哪些 事呢?弄懂事件的顺序将有助于我们在恰当的事件中做我们想做的事,也可以消除我们的一些混淆,比如把一些问题归咎给  web  程序的无状态之类的。

  

基础:新的编译模型和部分类(Partial Classes

asp.net  中的每一个  web form  都直接或间接的继承自  System.Web.UI.Page  类。一个  web from  包括两部分:一个是代码文件(WebForm.aspx.cs),它包括一些和page相关联的事件和方法,另一个是  aspx  文件,它包括一些  HTML  控件声明(在Visual Studio 2005  web  应用程序中,我们还有一个名为  WebForm.aspx.designer.cs  的设计类)

  asp.net 2.0中,我们不需要再定义控件变量,也不需要再在代码文件中写一些事件委托,这一切都要归功于部分类。在  asp.net 1.x  中,这些代码都会自动的在  InitializeComponent()里生成。但是到了2.0版本,runtime  将会创建一个部分类,这个类会包   aspx  页中的所有信息。这将使得代码文件非常清晰并且易于管理。

这将消除VS2003中的代码文件和  aspx  页面之间的名字相互联系 的改变(如果我们要改变任意控件的  ID,都不得不改变  aspx  页和代码文件)。在  VS2005中所有控件的事件都定义在  aspx  页里。所以代码文件中的事件委托和控件变量将被清除,这是比先前的  VS2003方便的地方。

页的生存周期

了解页的生存周期中的每一个请求是非常重要的,丢值、丢状态的问题都可能是你对页的生存周期了解不够造成的。当然,如果你要在  asp.net  保留状态的话,可以用诸如  ApplicationSessionCache,或者  Cookies  之类的

 

注意:asp.net 2.0中的视图状态由两部分组成,控件状态和视图状态。

下面我们将按照  web  程序的代码文件中的各个事件的触发顺序来详细的介绍它们

重点提示:除了  Init()  Unload()之外的所有事件都是从最外面到最里面被激发的。例如,一个用户控件的  init  事件在它的父页类的  Page_Init()事件之前被激发(译者注:这是从里到外)。

 

1.      PreInit()

在这个页面级的事件中,所有在设计时创建的控件都将被用默认值做初始化。例如,如果你有一个 Text  属性值为“Hello”  TextBox  控件,则此时这个属性被设置。我们也可以在这里动态的创建控件。

这个事件仅仅发生在页级别的类中,用户控件和母版页没有这个事件

下面的代码示例了如何重写这个方法以增加你的自定义代码

protected override void OnPreInit(EventArgs e)       
{
    
// custom code            
    base.OnPreInit(e);
}

 

注意,我们只能在  PreInit()事件中动态的设置  themes  使用母版页时的特例,我们先要了解一个非常重要的知识点——母版页被处理的过程就相当于内容页中的一个控件。

所以如果一个页有其相关联的母版页的话,那么在  PreInit()事件里页中的所有控件都不会被初始化。而只有在  Init()事件开始之后,你才能直接访问这些控件。为什么?

这个原因就是内容页中的所有控件都包含在“ContentPlaceholder”里,而“ContentPlaceholder”其实就是母版页的一个子 控件。现在母版页被处理的过程就相当于内容页中的一个控件,我们早先提到过,除了  Init()  Unload()之外的所有事件都是从最外面到最里面被激 发的。虽然页的  PreInit()是第一个被触发的事件,但是用户控件和母版页是没有这个事件的,所以在页的  Page_PreInit()方法中,母版页 和用户控件都不会被初始化,而是在  Init()事件之后

接下来让我们来看一下  Page_Init()事件之后控件的层次结构

 

2.      OnInit()

在这个事件里,我们能读出控件的属性(在设计模式中设置的)。但是我们不能读出用户设置的值,因为得到用户设置的值是在  LoadPostData()事件被激发之后。不过在这个事件中我们可以得到  POST  数据,如下

string selectedValue = Request.Form[controlID].ToString();

 

3.      LoadViewState

这个事件仅仅在回发之后被激发(IsPostBack == true)。在这个事件中  runtime  从隐藏域中分解出view state并加载到所有启用了  view state  的控件。

  

4.      LoadPostBackData

这个事件也仅仅是在回发之后被激发。

这个事件里实现了  IPostBackDataHandler  接口的控件从  HTTP    POST  数据中得到值。注意,textbox  控件不能从  view state  中获得值,而是在此事件中从  POST  数据中获得值。所以即使有些控件没有启用  view state,只要它实现了  IPostBackDataHandler  接口就可以从  HTTP    POST  数据中得到值。

另一个重要的知识点是 如果我们有一个  DropDownList  控件并动态的给它增加一些选择项,那么runtime  将不能得到这些值除非启用了  view state(即使控件继承自  IPostBackDataHandler  接口)。这个原因就是在  HTTP    POST  数据中的每一个控件只能有一个值,并且  POST  数据中的所有值都不会被保存,除了使用  view state

 

5.      Page_Load

这是最常用的方法了,而且是一些开发新手放置他们代码的第一个地方,有些新手们往往认为这就是Page  类第一个触发的方法。这个方法是混淆我们  Page  生存周期的罪魁祸首之一。

注意:如果页里有任何用户控件的话,那么用户控件的  Load  方法将在页类的  Load  方法之后被触发。这个原因早先已经解释过了,除了  Init()  Unload()之外的所有事件都是从最外面到最里面被激发的。所以页的  Page_Load()之后,页内的其它控件的  Load  方法才被触发。

 

6.      Control Event Handlers

事件处理(比如像  Button1_Click()之类的)是定义在  ASPX  页面中的,有一些开发人员认为当单击一个按钮后会立即出发  Button_Click() ,他们忘了在这个事件触发之前首先要触发  Page_Load

 

7.      PreRender

如果我们想改变某一个控件的值,这是最后的机会了

 

8.      SaveViewState

控件的  ViewState  被存储在  form  的隐藏域中

 

9.      Render

呈现

 

10. Unload

这是最后的清理操作
动态控件

现在我们已经知道了页的生存周期的重要事件,接下来让我们关注一下如何创建以及保持动态生成控件的状态。有的时候我们需要动态的生成控件,比如我原来管理的 一个酒店预订的项目,用户在一个  TextBox  里输入房间号,根据这个值动态的生成一个用户控件来显示该房间的详细信息。

开发人员虽然能 动态的生成用户控件,但是却不能保存用户控件的状态。当我看了代码后,他们把生成控件的代码写到了  Button    Click  事件里。根据我们上面所讨论的,Button_Click()  LoadViewState()  LoadPostData()之后触发,而控件的值是要在  view state    POST  数据中取得的。

所以除非在  Page_Init()  Pre_Init()方法里重新创建控件(它们发生在  LoadViewState  LoadPostData  之前),这样就可以在下一个事件里获得控件的值。

现在,如果把代码写到  Page_Init()事件里的话,将不能得到用户在  TextBox(它是一个静态控件)里输入的值。原因就在于这是  Page_Init()事件,控件的值被初始化为它们设计时的默认值,而不会得到用户输入的值

所以如果要在这里访问到用户输入的值话只有一个办法,就是从  POST  数据中取值。代码如下

protected override void OnInit(EventArgs e)    
{
    
// 通过Post数据得到用户在TextBox里输入的值
    string selectedValue ; 
    
if(Request.Form["txtNoOfRooms"] != null)               
        selectedValue = Request.Form["txtNoOfRooms"].ToString();
    
// 动态生成控件的代码
          
    
base.OnInit(e);
}

注意:感谢  ASP.NET  论坛的  Mike Banavige,有了他的帮助才让我增加了这部分内容。如果你在  Page_Load  事件里创建一个动态控件,并把它添加到  PlaceHolder    Panel  里(要打开  view state),那么动态控件将会维持它的状态,即使它不是在  Page_Init()中创建的,为什么?
因就是控件一旦被添加到页的控件树里,TrackViewState()方法就负责跟踪其状态。只要控件被添加到控件树里,这个方法就会被自动的触发。因为这个原因,对控件的任何修改(如添加  item  之类的)都应该在动态控件被添加到页的控件树之后来做,否则其状态将丢失。请看如下代码

protected void Page_Load(object sender, EventArgs e)
{
    
// 创建一个DropDownList
    DropDownList d = new DropDownList();
    
    
// TrackViewState()方法将被触发去跟踪这个DropDownList的状态,所以其状态将被保持
    PlaceHolder1.Controls.Add(d); 
    
if (!IsPostBack)
    
{
        d.Items.Add("test1");
        d.Items.Add("test2");
    }
}

下面的代码则不会保持动态控件的状态

protected void Page_Load(object sender, EventArgs e)
{
    
// 动态创建一个控件 
    dropdownDropDownList d = new DropDownList();
    
if (!IsPostBack)
    
{
        d.Items.Add("test1");
        d.Items.Add("test2");
    }
    
    
// "test1""test2"值将丢失
    PlaceHolder1.Controls.Add(d); 
}

总结

我已经解释了页的生存周期的一些相关事件及其重要性,同时我也会不定期更新这篇文章以增加一些小提示和小技巧,此外也欢迎读者指出本文的缺陷之处及修改建议

记住页的整个生存周期的各个事件的顺序是非常重要的,这样我们就可以根据不同的需求在合适的位置写出相应的代码。 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值