asp.net页面生存周期及事件

来源:互联网 发布:淘宝不花钱买东西退款 编辑:程序博客网 时间:2024/05/21 20:30

ASP.NET 页运行时,此页将经历一个生命周期,在生命周期中将执行一系列处理步骤。这些步骤包括初始化、实例化控件、还原和维护状态、运行事件处理程序代码以及进行呈现。了解页的生命周期非常重要,这样就能在合适的生命周期阶段编写代码,以达到预期效果。此外,如果开发自定义控件,则必须熟悉页生命周期,从而正确地初始化控件,使用视图状态数据填充控件属性以及运行所有控件行为逻辑。(控件的生命周期基于页的生命周期,但是页引发的控件事件比单独的 ASP.NET 页中可用的事件多。)

常规页生命周期阶段
一般来说,页要经历下表概述的各个阶段。除了页生命周期阶段以外,还有在请求前后出现的应用程序阶段,但是这些阶段并不特定于页。有关更多信息,请参见 ASP.NET 应用程序生命周期概述。

阶段
说明

页请求
页请求发生在页生命周期开始之前。用户请求页时,ASP.NET 将确定是否需要分析和编译页(从而开始页的生命周期),或者是否可以在不运行页的情况下发送页的缓存版本以进行响应。

开始
在开始阶段,将设置页属性,如 Request 和 Response。在此阶段,页还将确定请求是回发请求还是新请求,并设置 IsPostBack 属性。此外,在开始阶段期间,还将设置页的 UICulture 属性。

页初始化
页初始化期间,可以使用页中的控件,并将设置每个控件的 UniqueID 属性。此外,任何主题都将应用于页。如果当前请求是回发请求,则回发数据尚未加载,并且控件属性值尚未还原为视图状态中的值。

加载
加载期间,如果当前请求是回发请求,则将使用从视图状态和控件状态恢复的信息加载控件属性。

验证
在验证期间,将调用所有验证程序控件的 Validate 方法,此方法将设置各个验证程序控件和页的 IsValid 属性。

回发事件处理
如果请求是回发请求,则将调用所有事件处理程序。

呈现
在呈现期间,视图状态将被保存到页,然后页将调用每个控件,以将其呈现的输出提供给页的 Response 属性的 OutputStream。

卸载
完全呈现页、将页发送至客户端并准备丢弃时,将调用卸载。此时,将卸载页属性(如 Response 和 Request)并执行清理。

生命周期事件
在页生命周期的每个阶段中,页将引发可运行您自己的代码进行处理的事件。对于控件事件,通过以声明方式使用属性(如 onclick)或以使用代码的方式,均可将事件处理程序绑定到事件。

页还支持自动事件连接,即,ASP.NET 将寻找具有特定名称的方法,并在引发特定事件时自动运行这些方法。如果 @ Page 指令的 AutoEventWireup 属性设置为 true(或者如果未定义该属性,因为默认情况下为 true),页事件将自动绑定至使用 Page_event 命名约定的方法,如 Page_Load 和 Page_Init。有关自动事件连接的更多信息,请参见 ASP.NET Web 服务器控件事件模型。

下表列出了最常用的页生命周期事件。实际的事件比列出的事件要多。但是,它们不用于大多数页处理方案。而是主要由 ASP.NET 网页上的服务器控件使用,以初始化和呈现它们本身。如果要编写自己的 ASP.NET 服务器控件,则需要详细了解这些阶段。有关创建自定义控件的信息,请参见开发自定义 ASP.NET 服务器控件。

页事件
典型使用

Page_PreInit
·使用 IsPostBack 属性确定是否是第一次处理该页。

·创建或重新创建动态控件。

·动态设置主控页。

·动态设置 Theme 属性。

·读取或设置配置文件属性值。

注意:如果请求是回发请求,则控件的值尚未从视图状态还原。如果在此阶段设置控件属性,则其值可能会在下一阶段被改写。

Page_Init
·读取或初始化控件属性。

Page_Load
·读取和更新控件属性。

Control events
执行特定于应用程序的处理:

·如果页包含验证程序控件,请在执行任何处理之前检查页和各个验证控件的 IsValid 属性。

·处理特定事件,如 Button 控件的 Click 事件。

Page_PreRender
·对页的内容进行最后更改。

Page_Unload
执行最后的清理工作,可能包括:

·关闭打开的文件和数据库连接。

·完成日志记录或其他特定于请求的任务。

注意:在卸载阶段,页及其控件已被呈现,因此无法对响应流做进一步更改。如果尝试调用方法(如 Response.Write 方法),则该页将引发异常。

其他的页生命周期注意事项
请注意有关页生命周期的以下附加信息:

·各个 ASP.NET 服务器控件都有自己的生命周期,该生命周期与页生命周期类似。例如,在相应的页事件期间将调用控件的 Init 和 Load 方法。如果页上包含控件,则将首先调用控件的 Init 方法,然后再调用页的 Init 方法。但是,将在调用控件的 Load 方法之前先调用页的 Load 方法。

·通过处理控件的事件,可以自定义控件的外观或内容。例如,所有的控件都将引发 Init、Load 和 Unload 事件,但是页开发人员通常不处理这些事件。而是通常处理特定于控件的事件,如 Button 控件的 Click 事件和 ListBox 控件的 SelectedIndexChanged 事件。在某些情况下,可能也需处理控件的 DataBinding 或 DataBound 事件。有关更多信息,请参见各个控件的类参考主题以及开发自定义 ASP.NET 服务器控件。

除了处理由页引发的事件以外,还可以重写页的基类中的方法。例如,可以重写页的 InitializeCulture 方法,以便动态设置区域性信息。注意,在使用 Page_event 语法创建事件处理程序时,将隐式调用基实现,因此无需在方法中调用它。例如,无论是否创建 Page_Load 方法,始终都会调用页基类的 OnLoad 方法。但是,如果使用 override 关键字(在 Visual Basic 中为 Overrides)重写页的 OnLoad 方法,则必须显式调用基方法。例如,如果在页中重写 OnLoad 方法,则必须调用 base.Load(在 Visual Basic 中为 MyBase.Load)以运行基实现。

1. PreInit()
在这个页面级的事件中,所有在设计时创建的控件都将被用默认值做初始化。例如,如果你有一个Text属性值为"Hello"的TextBox控件,则此时这个属性被设置。我们也可以在这里动态的创建控件。
这个事件仅仅发生在页级别的类中,用户控件和母版页没有这个事件。
下面的代码示例了如何重写这个方法以增加你的自定义代码
protected override void OnPreInit(EventArgs e)
{
//custom code
base.OnPreInit(e);
}
注意,我们只能在PreInit()事件中动态的设置themes,且应该在PreInit()事件或该事件之前加载母版页。

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

如果你在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)
{
// 动态创建一个控件
DropDownList d = new DropDownList();
if (!IsPostBack)
{
d.Items.Add("test1");
d.Items.Add("test2");
}

// "test1"和"test2"值将丢失
PlaceHolder1.Controls.Add(d);
}

 

看以下的问题:

http://bbs.csdn.net/topics/300097152

 

C# code
?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    protected void Page_PreInit(object sender, EventArgs e)
    {
        if (Session["selected_theme"] != null)
            Response.Write(Session["selected_theme"]);
        else
            Response.Write("session is null");
    }
  
    protected void Page_Load(object sender, EventArgs e)
    {
        Label1.Text = DateTime.Now.ToString();
    }
  
    protected void DropDownList1_SelectedIndexChanged(object sender, EventArgs e)
    {
        Session.Add("selected_theme", DropDownList1.SelectedValue);
    }


我想的是在一个下拉菜单(AutoPostBack=true)中进行选择,选中的值存入Session,然后在下次页面加载(由回发产生)时将session里的值打出来。

结果居然是:
第一次(首次加载):session is null;
第二次(选了dropdown):session is null;但是加载时间变了(我加一个标签显示当前时间)
第三次(再选dropdown):打印出我上次选的值。即如果我第二次选的blue,当时是没反应的;第三次才显示blue,但实际上我选的是green;

 

因为点击

SelectedIndexChanged 的执行顺序是:

Page_PreInit 
然后Page_Load 
然后DropDownList1_SelectedIndexChanged


所以
点击完,并没有立即把需要的值放到Seesion 中去,而是第二次,第三次才有之前的

为什么?因为状态丢失了,所以用一个Panel装起来,跟踪它的状态即可以了

 

原创粉丝点击