介绍
弄懂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保留状态的话,可以用诸如Application,Session,Cache,或者Cookies之类的

注意:asp.net 2.0中的视图状态由两部分组成,控件状态和视图状态。详细了解请参考这篇文章
http://msdn2.microsoft.com/en-us/library/1whwt1k7(VS.80).aspx

下面我们将按照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()事件之后控件的层次结构

http://hi.csdn.net/attachment/201005/25/788374_1274755772iglI.jpg

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);
}

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

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

asp.net 2.0中页的生存周期(Lifecycle)和动态控件[转]相关推荐

  1. [翻译]在Asp.net 2.0中操作数据::母板页和站点导航

    在Asp.net 2.0中操作数据::母板页和站点导航 英文原文 |  下载示例代码(C#)  |  翻译进度  |  英文教程目录 导言 通常,用户友好的个性化站点都有着一致的,站点统一的页面布局和 ...

  2. ASP.NET 2.0 中实现跨页提交 (碧血黄沙)

    在ASP.NET 2.0中,对于跨页提交已经有了非常合理的解决方案 下面是一个示例: BeginPage.aspx: 请注意Button1的PostBackUrl属性设置 <%@ Page La ...

  3. [导入]ASP.NET 2.0 中实现跨页提交 (碧血黄沙)

    在ASP.NET 2.0中,对于跨页提交已经有了非常合理的解决方案 下面是一个示例: BeginPage.aspx: 请注意Button1的PostBackUrl属性设置 <%@ Page La ...

  4. 在ASP.NET 2.0中使用样式、主题和皮肤

    ASP.NET 2.0的主题和皮肤特性使你能够把样式和布局信息存放到一组独立的文件中,总称为主题(Theme).接下来我们可以把这个主题应用到任何站点,用于改变该站点内的页面和控件的外观和感觉.通过改 ...

  5. ASP.NET 2.0 中配合 Master Page 使用的优化 CSS 模型

    ASP.NET 2.0 中增加了内建的 MasterPage 的支持,这对我们来说是一个很大的便利.然而经过一段时间的使用,我发现 MasterPage 并不是那么完美:嵌套的 MasterPage ...

  6. 在ASP.NET 2.0中操作数据教程系列

    在ASP.NET 2.0中操作数据教程系列 Scott Mitchell最近完成了为 www.asp.net 网站撰写的<在ASP.NET 2.0中操作数据 (Working with Data ...

  7. 第一次来,试发一帖!--ASP.NET 2.0 中的SqlCacheDependency特性

    ASP.NET中的Page Cache是个很有用的东东,只要简单的在页面上方加上一个OutputCache标签,就可以让页面在制定的Duration内直接把自动保存在缓存中的页面内容输出,而不需要让A ...

  8. [翻译]在asp.net 2.0中使用WebParts

    原文地址:http://dotnetslackers.com/articles/aspnet/UsingWebPartsInASPNet20.aspx [译者改后代码下载] [翻译]在asp.net ...

  9. Asp.Net 2.0中的客户端脚本

    在使用 ASP.NET 的时候,我们仍然在许多情况下需要使用客户端脚本.以下是笔者根据自己的经验和一些粗浅的研究,对此作一个简要的总结. 一.在 HTML 里直接写脚本 这个方法是最简单的,直到如今我 ...

  10. ASP.NET 2.0 中改进的缓存功能

    摘要:本文中,Stephen Walther 将重点介绍 ASP.NET 2.0 中新增的缓存功能,以及如何使用这些新功能改进 ASP.NET 应用程序的性能和可扩展性.(本文包含一些指向英文站点的链 ...

最新文章

  1. python获取当前目录路径和文件
  2. 用PHP控制Nagios进程
  3. [solr] - solr5.2.1环境搭建 - 使用solr自带的jetty服务器
  4. C语言和设计模式(装饰模式)
  5. java word2007_Java解析word2007、Excel2003和Excel2007
  6. Phalcon调试大杀器之phalcon-debugbar安装
  7. GTJ2018如何导出全部工程量_新清单计量规范征求意见稿第二期来啦!来看看那些让你烦恼的操作如何解决...
  8. php代码,IP地址归属地批量查询功能
  9. Pillow 10行代码给营业执照模板写数据,批量生产
  10. python正则匹配中文或数字_Python匹配中文的正则表达式
  11. 蒟蒻退役记————————————(3)
  12. 方舟破解版自建服务器,方舟生存进化自建服务器教程
  13. 六安构建智慧城市节水管理平台
  14. 如何把多张图片合并成一个PDF?
  15. japonensisJava新地址
  16. 8Manage:大宗商品采购,专注构建企业采购信息化!
  17. 修改linux中@后的名字
  18. FL Studio教程之Fruity Blood Overdrive插件
  19. 6-4 使用函数求素数和 (20 分)
  20. 蓝牙LE Audio的关键-LC3技术

热门文章

  1. mysql资源估算_关于数据库查询要耗费的服务器资源估算!高手进~
  2. Oracle的sql条件带有,oraclesql语句的if
  3. html超链接点不了_Html使文本可点击而不会超链接
  4. python:拼接多个csv文件
  5. matlab中的矩阵元素错位,matlab – 如何将矩阵中较弱元素附近的弱元素归零?
  6. python基础:列表(list)
  7. 大数据MongoDB之NoSQL的CAP定理和BASE原则是什么?
  8. 教育行业oa软件怎么样?
  9. Java并发编程基础-锁-synchronized状态变换
  10. HDFS的dfs.replication不同验证