理论文章页生命周期.docx
- 文档编号:9727532
- 上传时间:2023-02-06
- 格式:DOCX
- 页数:22
- 大小:104.24KB
理论文章页生命周期.docx
《理论文章页生命周期.docx》由会员分享,可在线阅读,更多相关《理论文章页生命周期.docx(22页珍藏版)》请在冰豆网上搜索。
理论文章页生命周期
Asp.Net2.0中的页生命周期
2007/08/25
摘要:
对于页生命周期的不了解会带来错误的ASP.Net编程。
ASP.Net为Code-Behind和Event-Driven编程模式。
每次客户端对页面的请求就会开始一次新的页生命周期,而Web页面是一种无状态的。
为此,ASP.Net提出视图状态用于在每次回发请求之间保存控件的属性值,以克服无状态带来的问题。
在了解页生命周期的基础上提出ASP.Net编程的指导性建议。
关键词:
ASP.Net;页生命周期(PageLifeCycle);视图状态(ViewState);回发请求(PostBackRequest);无状态(Stateless)
1.引述1
2.ASP.Net的编程模型3
2.1.Code-Behind的编程3
2.2.Event-Driven的编程4
3.页面的请求4
3.1.ASP.Net标记的呈现(ASPTagRender)4
3.2.视图状态(ViewState)5
3.3.页请求的类别5
4.页生命周期6
5.改进的例子与编程建议10
6.结束语14
1.引述
页生命周期是Asp.Net中的重要概念,如果在不清楚页生命周期的概念下,你编写的Web程序很容易出现一些奇怪的问题,甚至是错误。
这可以通过下面的例子说明。
如果你想开发一个Web用户控件,该控件类似于Windows应用中用到的“数字旋钮”控件,通过一个可调节按钮改变一个输入数字的大小。
这个Web用户控件具有一个“TextBox”控件,两个“Button”控件。
具体的控件设置如下表:
表1页面控件及属性设置
控件类型
控件属性
属性取值
说明
TextBox
ID
txtNumeric
记录数字的文本框
Text
0
Button
ID
btnUp
上调数字
Text
↑
Button
ID
btnDown
下调数字
Text
↓
该Web控件看起来如下图:
图1用户控件的外观
当我们按动“
”按钮的时候,“txtNumeric”文本框的数字会自动增长一个整数1;而按动“
”按钮的时候,“txtNumeric”文本框的数字会自动减少一个整数1。
为了达到这一效果,需要在Web用户控件的后台代码中为“btnUp”和“btnDown”两个Button控件的“Click”事件编写代码,以及为Web用户控件定义一个公有属性“Numeric”,具体代码如下:
代码1用户控件的后台类代码
publicpartialclassWUCNumericTuner:
System.Web.UI.UserControl
{
#regionProperties
///
///用于提供对外的接口,获得当前数字文本所记录的数字。
///
publicintNumeric
{
get
{
returnConvert.ToInt32(this.txtNumeric.Text);
}
set
{
this.txtNumeric.Text=value.ToString();
}
}
#endregion
protectedvoidPage_Load(objectsender,EventArgse)
{
}
///
///上调按钮的Click事件。
///
///
///
protectedvoidbtnUp_Click(objectsender,EventArgse)
{
this.Numeric+=1;
}
///
///下调按钮的Click事件。
///
///
///
protectedvoidbtnDown_Click(objectsender,EventArgse)
{
this.Numeric-=1;
}
}
这段代码中实际定义了该Web用户控件的后台代码,记住该Web用户控件的名称是“WUCNumericTuner”。
现在可以使用该Web用户控件构件一个页面,用来求解两个整数相加运算,具体的页面如下图:
图2使用“WUCNumericTuner”用户控件构建的页面
在该页面上放置了两个刚才创建的Web用户控件“WUCNumericTuner1”和“WUCNumericTuner2”,并放置了一个“Label”控件用来记录求和的结果。
这需要在页面的“Page_Load”事件中编写如下代码:
代码2页面的后台类代码
publicpartialclassTestLifeCycle:
System.Web.UI.Page
{
protectedvoidPage_Load(objectsender,EventArgse)
{
this.lblSum.Text=(this.WUCNumericTuner1.Numeric+
this.WUCNumericTuner2.Numeric).ToString();
}
}
但是,当我们按动两个“WUCNumeric”控件的调整按钮时候,会发现一个奇怪的现象,请仔细看下图:
图3所看到的奇怪结果
你们会发现,无论如何调整,求和的结果总是比实际相加的两个数求和的结果小一个数或者会大一个数,即求解的总是上一次调整的两个数字的相加之和。
就象上图中“3+2=4”的错误结果。
这到底是怎么回事呢?
为了解释这个问题,就必须从头开始了解Asp.Net的页生命周期的概念。
2.ASP.Net的编程模型
2.1.Code-Behind的编程
无论是一个ASP.Net页面还是一个Web用户控件,都是由两部分组成,即一个“xxx.aspx”或“xxx.ascx”页面标记文件,同时还有一个与之对应的后台“xxx.aspx.cs”或“xxx.ascx.cs”代码文件。
我们把后台代码文件就称为Code-Behind。
这是ASP.Net将页面表现与页面逻辑分离的结果。
但是最终这两者在浏览器请求页面访问的时候会合并成一个整体响应一次请求,并会将请求的结果以“Html”标记流的形式回送给浏览器,以展现页面。
图4一次页面请求
2.2.Event-Driven的编程
而ASP.Net精道之处在于,它模仿Windows应用程序下的编程方式,一切由控件的交互事件驱动整个程序的运行。
同样,在ASP.Net的页面中可以放置很多预定义的控件,也可以放置自己定义的控件,这些控件都有自己的交互事件,而承载这些控件的页面就象窗体容器,因此,ASP.Net页面也称为“Web窗体”。
例如:
“Button”控件有“Click”事件。
同时,页面也有自己的事件,如:
“Page_Load”事件。
3.页面的请求
无论是ASP.Net技术,还是Java的JSP技术,以及其他的什么PHP技术,都是基于Html标记和Http协议这些已成事实的国际标准来构建Web应用的。
这需要我们搞清楚两个事实:
①所有这些动态网页构建技术最终都是在做一件事情,即把它们的特殊页面标记或后台程序的运行结果,翻译和转换成浏览器只认识的“Html”标记语言;②所有从浏览器发送的页面浏览请求都是一次性的,不会在浏览器端留下任何程序运行过程中的内存信息,我们称这为“无状态”的请求,所有的程序计算都发生在服务器端的一次请求过程中。
这一点可以说是Internet存在的固有特点所要求的,即不能长时间由一个客户端占据着和服务器之间的网络资源,同时也为我们编程带来了极大的麻烦,因为整个程序的运行要得到最终结果,需要很多中间过程,而“无状态”的情况会让程序丢失中间步骤的运算结果,而无法得到正确的最终结果。
对于上述两个事实,ASP.Net技术都为我们提供了很好的支持。
具体来说包括:
标记呈现技术和视图状态技术。
3.1.ASP.Net标记的呈现(ASPTagRender)
我们看到在ASP.Net的页面文件(xxx.aspx/xxx.ascx)中有很多类似于“asp:
Button”这样的标记,它们最终在一次页请求的最后阶段要被翻译成一个或几个标准的“Html”标记。
例如:
上述两个“TextBox”控件在页面文件中就是以“asp:
Button”标记定义的。
它们最终在浏览器中查看源文件的时候会变成如同下方的一段“Html”代码:
代码3ASP.Net标记被呈现为Html代码
id="WUCNumericTuner1_btnUp"/> 3.2.视图状态(ViewState) 而为了能够记录一个页面在程序运行期间得到的中间结果,提出了“视图状态”的概念。 它实际上通过一个特殊的“Html”标记记录下每次运行的中间结果。 这个特殊的“Html”标记如下: 代码4视图状态在Html中的呈现 value="/wEPDwUKMTEyNDAzMzYwMw9kFgICAw9kFgICBQ8PFgIeBFRleHQFATBkZGQ+Un61OAiXnW7Zrz37EiJnbfsxfg=="/> 这是一个“隐藏域”的“Html”标记。 它的值是被加密了的字符串,主要是记录下控件或页面的属性取值。 在每次页面请求的过程中都要被自动恢复到控件上去,这样就能把“无状态”的页面请求变成所谓的“有状态”的页面请求。 在Page和每个控件的类定义中,都有一个受保护的成员属性“ViewState”用来记录它们的属性值,并在每次页请求的初期从“ViewState”中恢复上次页请求时记录下来的取值到页面或控件的属性上,以备当前请求之用。 而页面上的很多控件都具有与用户交互的特性,例如: 文本框控件能够接受用户的输入,并将其回发给服务器。 但同时,很多控件的属性在设计时可以指定一个初始值,这样就产生了两种视图状态: 静态页视图状态;动态页视图状态。 (1)静态页视图状态 控件的属性在设计时给定的取值。 这些取值在页面被初始化之前就被赋值给控件的属性。 (2)动态页视图状态 控件的属性在运行时由用户从UI输入的值,或者用户在后台代码中给与的赋值。 对于前者来说,用户输入的值会在页面初始化之后,页面装载之前被自动恢复给控件的属性。 而对于后者来说,在页面卸载之前还可以通过赋值改变控件的属性值,以呈现到浏览器中,而在卸载阶段赋值,则无法将改变之后的属性值呈现给浏览器。 3.3.页请求的类别 对于同一个页的请求分为两种: 初次请求;回发请求。 对于页和控件,都有一个公共属性IsPostBack,用来标识页请求的类别。 (1)初次请求(FirstRequest) 页面第一次加载到浏览器中,为初次请求。 一般地,用户通过在浏览器的地址栏内输入页面Url地址发出的请求,通过用户点击超文本链接而转到该页面的请求都是初次请求。 可以通过判断“IsPostBack==false”来确定当前页面的请求为初次请求。 (2)回发请求(PostBackRequest) 当用户与页面上的控件进行交互而引起的当前页面刷新,将当前页面回送给服务器,再次对该页面的请求为回发请求。 可以通过判断“IsPostBack==true”来确定当前页面的请求为回发请求。 例如: Button控件的Click事件,是由用户单击Button控件引发的,这就会引起回发请求。 4.页生命周期 从程序代码的角度上看,每个页面实际上是一个类,每次页请求,就会进行一次类的实例化,以及按照特定顺序和规则调用其定义的事件方法的过程。 所以每次页请求的时候,控件属性都会恢复到初始值上去,这也是从代码角度看到的“无状态”的情况。 而这一过程也是我们称之为的“页生命周期”概念。 那么到底一个页面可以定义多少个事件方法? 并且它们是按照什么顺序和规则被调用的呢? 图5详细描述了页面和控件所具有的所有事件以及调用顺序。 图5页面与控件事件及调用顺序 可以看出页的事件要比控件的事件多,更加丰富。 需要注意: ①控件的Init事件发生在页的Init事件之前;②控件的Load事件发生在页的Load事件之后;③控件的PreRender事件发生在页的PreRender事件之后;④控件的Unload事件发生在页的Unload事件之前。 ⑤页和控件的回发请求的事件在各个控件的Load之后发生,如: Button控件的Click事件;⑥在页和控件的回发请求事件调用之前,会进行验证,并自动为页的“IsValid”赋值;⑦数据绑定发生在各个控件的PreRender事件之后。 下表给出了整个页生命周期的各个阶段,以及发生的顺序: 表2整个页生命周期的各个阶段及发生顺序 阶段 说明 用户可否访问 页/控件事件 页请求 页请求发生在页生命周期开始之前。 用户请求页时,ASP.NET将确定是否需要分析和编译页(从而开始页的生命周期),或者是否可以在不运行页的情况下发送页的缓存版本以进行响应。 不可以 开始 在开始阶段,将设置页属性,如Request和Response。 在此阶段,页还将确定请求是回发请求还是新请求,并设置IsPostBack属性。 此外,在开始阶段期间,还将设置页的UICulture属性。 不可以 页初始化 页初始化期间,可以使用页中的控件,并将设置每个控件的UniqueID属性。 此外,任何主题都将应用于页。 如果当前请求是回发请求,则回发数据尚未加载,并且控件属性值尚未还原为视图状态中的值。 可以 Page_PreInit Page_Init Page_InitComplete 加载 加载期间,如果当前请求是回发请求,则将使用从视图状态和控件状态恢复的信息加载控件属性。 可以 Page_PreLoad Page_Load Page_LoadComplete 验证 在验证期间,将调用所有验证程序控件的Validate方法,此方法将设置各个验证程序控件和页的IsValid属性。 不可以 回发事件处理 如果请求是回发请求,则将调用所有事件处理程序。 可以 ControlPostBackEvents 预呈现 做呈现前的最后准备工作,如绑定数据源数据到数据绑定控件上。 可以 Page_PreRender Page_PreRenderComplete Page_SaveStateComplete 呈现 在呈现期间,视图状态将被保存到页,然后页将调用每个控件,以将其呈现的输出提供给页的Response属性的OutputStream。 不可以 卸载 完全呈现页、将页发送至客户端并准备丢弃时,将调用卸载。 此时,将卸载页属性(如Response和Request)并执行清理。 可以 Page_Unload 在这里要说明几个问题: ①呈现(Render)是指将页面和控件转换成Html的过程;②在页生命周期的所有阶段中“页请求”、“开始”、“验证”和“呈现”几个阶段,我们是无法通过页或控件的事件来访问和进行控制的。 那么我们又是如何通过事件方法来获取或者改变在各个阶段页和控件的属性值的呢? 微软给出了以下的建议: 表3页事件及典型使用 页事件 典型使用 Page_PreInit ∙使用IsPostBack属性确定是否是第一次处理该页。 ∙创建或重新创建动态控件。 ∙动态设置主控页。 ∙动态设置Theme属性。 ∙读取或设置配置文件属性值。 注意 如果请求是回发请求,则控件的值尚未从视图状态还原。 如果在此阶段设置控件属性,则其值可能会在下一阶段被改写。 ∙ Page_Init ∙读取或初始化控件属性。 Page_Load ∙读取和更新控件属性。 Controlevents 执行特定于应用程序的处理: ∙如果页包含验证程序控件,请在执行任何处理之前检查页和各个验证控件的IsValid属性。 ∙处理特定事件,如Button控件的Click事件。 Page_PreRender ∙对页的内容进行最后更改。 Page_Unload 执行最后的清理工作,可能包括: ∙关闭打开的文件和数据库连接。 ∙完成日志记录或其他特定于请求的任务。 注意 在卸载阶段,页及其控件已被呈现,因此无法对响应流做进一步更改。 如果尝试调用方法(如Response.Write方法),则该页将引发异常。 ∙ 我们在微软建议的基础上给出几个关键点和自己的编程建议: 表4页事件的关键点与编程建议 页事件 关键点 建议 Page_PreInit (1)IsPostBack已经被赋予正确值。 (2)静态视图状态已经被恢复。 动态设置页主题。 Page_PreLoad (1)动态视图状态已经被恢复。 根据自己的需要和数据,初始化页面控件。 这包括三种情况: 只在初次请求需要初始化的;只在回发请求需要初始化的;每次请求需要初始化的。 Page_SaveStateComplete (1)完成了视图状态的保存。 最后一个可以改变控件属性的事件。 我们可以通过为上述那个有一点小错误的例子,设置调试断点,观察所说的关键点。 测试的用例为三个: ①让页面初次加载,我们标记为“初次请求”; ②点击“WUCNumeric1”用户控件的“ ”按钮,我们标记为“回发请求 (1)”; ③在“WUCNumeric1”用户控件的“txtNumeric”文本输入框内输入“5”,我们标记为“回发请求 (2)”。 经过调试观察到的结果如下表: 表5调试的结果 页事件 控件事件 初次请求 回发请求 (1) 回发请求 (2) Page_PreInit IsPostBack: false WUCNumericTuner1.Numeric: 0 IsPostBack: true WUCNumericTuner1.Numeric: 0 IsPostBack: true WUCNumericTuner1.Numeric: 0 Page_PreLoad WUCNumericTuner1.Numeric: 0 WUCNumericTuner1.Numeric: 0 WUCNumericTuner1.Numeric: 5 Page_Load WUCNumericTuner1.Numeric: 0 WUCNumericTuner1.Numeric: 0 WUCNumericTuner1.Numeric: 5 Page_Load txtNumeric.Text: 0 txtNumeric.Text: 0 txtNumeric.Text: 5 btnUp_Click txtNumeric.Text: 0 txtNumeric.Text: 1 txtNumeric.Text: 6 Page_LoadComplete WUCNumericTuner1.Numeric: 0 WUCNumericTuner1.Numeric: 1 WUCNumericTuner1.Numeric: 6 通过调试我们可以看到: 对于“txtNumeric”控件的“Text”属性的静态视图状态“0”在页的“Page_PreInit”事件引发时就已经恢复了,而通过“ ”按钮赋值后产生的动态视图状态“1”是在“WUCNumericTuner1”的“btnUp_Click”事件中才得到,而我们在页事件“Page_Load”中编写了代码: “this.lblSum.Text=(this.WUCNumericTuner1.Numeric+this.WUCNumericTuner2.Numeric).ToString();”,以此得到两者相加的结果,但是页事件“Page_Load”是在“btnUp_Click”事件之前就被引发的,因此我们怎样都得不到正确的相加结果。 同样地,当我们在“WUCNumeric1”的文本框内输入一个数“5”,所产生的是一个交互方式的动态视图状态,而这个状态是在页的“Page_PreLoad”事件引发之前被恢复的,同时在“btnUp_Click”事件中进一步被代码增加了一个“1”,最终变成了“6”,可两者相加的求和代码仍然是在页的“Page_Load”事件中,因此,也是无法得到正确的求和结果的。 这就是开篇的时候看到的错误的原因所在。 通过上述的例子,可以看到,我们了解“页生命周期”的概念,对于正确编写ASP.Net程序来说是十分重要的。 接下来,我们给出编写ASP.Net页面程序和开发Web用户控件的一些建议。 5.改进的例子与编程建议 现在,我们来改进开篇的例子,让它能够正常工作。 为此,我们只需要将代码“this.lblSum.Text=(this.WUCNumericTuner1.Numeric+this.WUCNumericTuner2.Numeric).ToString();”移入页的事件“Page_LoadComplete”中。 代码5求解两数相加的结果 protectedvoidPage_LoadComplete(objectsender,EventArgse) { this.lblSum.Text=(this.WUCNumericTuner1.Numeric+ this.WUCNumericTuner2.Numeric).ToString(); } 然后,我们让用户控件“WUCNumericTuner”功能增强一些,为它增加一个属性“Step”表示调整的步长。 开始的例子中,一次调整只能改变一个单位的整数“1”,这是很难满足用户需求的。 为此,我们在“WUCNumericTuner”控件的后台代码中,增加以下代码段: 代码6“WUCNumericTuner”增加的属性“Step” privateintm_Step=1; [Browsable(true)] [Category("可访问性")] [Description("调整步长")] publicintStep { get { returnm_Step; } set { m_Step=value; } } 稍微解释一下,上述代码中定义了一个私有成员变量“m_Step”用于记录调整步长,而属性“Step”不仅封装了“m_Step”私有成员变量,而且其上面的代码是给属性“Step”定义了元数据,用于在控件属性设计器中为其设置静态视图状态,具体见下图: 图6属性设计器中设置属性静态视图状态 同时,我们将“ ”和“ ”两个按钮的“Click”事件改造为以下代码: 代码7调整按钮的Click
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 理论 文章 生命周期