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 |
|
||
Page_Init |
|
||
Page_Load |
|
||
Control events |
執行特定于應用程序的處理: |
||
Page_PreRender |
|
||
Page_Unload |
執行最后的清理工作,可能包括:
|
其他的頁生命周期注意事項
請注意有關頁生命周期的以下附加信息:
-
各個 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)以運行基實現。
-
初始化(Initialization)
頁面被請求時,第一個被執行的總是構造函數(constructor). 你可以在這里初始化很多自定義屬性或對象。不過這里有一些限制,因為 page 還沒有被完全初始化。特別地,你必須使用 HttpContext.Current 來訪問 QueryString, Form, Cookies 集合,以及 Cache 對象。而 Session 對象在 constructor 里是無法訪問的。
下面接著執行的是 AddParsedSubObject 方法,這個方法把組成該 page 的所有子控件添加到控件集合樹中。在很多高級的頁面模板解決方案中,該方法通常被覆蓋,以便把頁面的控件添加到一個特殊的頁面模板中去。該方法遞歸的被子控件調用,所有這些子控件都是這時候初始化的,從最里面的開始。
接著是 DeterminePostBackMode 方法。該方法允許你影響 IsPostBack 的值,以及相關事件。如果你想從數據庫中加載 ViewState 以便 redirect 時,這個可能對你有用。因為 ViewState 僅僅在 IsPostBack 為 true 的時候被恢復。 你可以通過返回 null 來強制不 postback, 或者返回 Request.Form 來強制 postback. 這個方法是不推薦使用的,除非是在特殊的情況下,因為他還影響其他的事件。
然后是 OnInit 方法。通常這是我們使用到的第一個方法。這時,所有控件已經被初始化,也就是說所有原始值都被設定了。而 ViewState 以及所有其他 post 的值還沒有被應用到控件上。也就是說這時候所有通過代碼或者用戶操作做的更改還沒有被恢復。這通常是創建或重新創建動態控件的最佳時機。
恢復和加載(Restore and Load)
接下來的 LoadPageStateFromPersistenceMedium 方法,僅僅在 PostBack 時被執行。當你要改變保存 ViewState 的方法時(使用 Session 或其他自定義的儲存方法),覆蓋這個方法,以及后面的 SavePageStateToPersistenceMedium 方法。注意:該方法并不真正加載 ViewState 到 page 及其子控件。
ViewState 被取回后,接著 LoadViewState 方法將它們恢復到 page, 并遞歸的恢復到每一個子控件(只有 PostBack 的那些).這時,每個控件已經被恢復到了它上次執行時的狀態,但用戶 post 的值還沒有被應用。因為這屬于 ViewState. 這個方法是恢復所有在事件中創建的動態控件的最好時機。
下一個是 ProcessPostData 方法。僅僅在 PostBack 時被執行。而且這個方法不能被覆蓋,因為它是頁面基類中實現的一個私有方法。這個方法最終將用戶 post 的值,通過匹配控件的名稱的方法,恢復到頁面。這時,page 已經被完全恢復了。動態控件必須在這個方法之前被創建。這個方法同時也為稍后的 changed 事件記錄控件值的改變。
然后才是 OnLoad 方法。大部分的代碼中都使用這個方法,因為這是在 page 的生命周期中,第一個所有的值都被恢復了的地方。我們可以通過檢查 IsPostBack 屬性來避免不必要的重設狀態。同時也可以檢查 IsValid 屬性來進行驗證。同時還可以在這里創建動態控件。所有這些控件的方法都會被執行并捕獲,包括 ViewState. 但回發的值不可以。
Raised Events
下一個方法,ProcessPostData 方法,實際上是前面那個方法的第二個入口(second pass)。它僅僅處理回發,而且由于是私有方法,所以不能被覆蓋。這個方法顯得有些奇怪,但又是必要的。因為在 OnLoad 方法中重建的動態控件需要他們回發的值。所有在這個方法之后創建的動態控件,將只能恢復 ViewState, 而不能恢復回發的值,并且不能觸發任何更改事件。
下一個方法, RaiseChangedEvents, 同樣僅僅用于回發時。它是一個基類實現的私有方法。這時 changed 事件被真正觸發。這基于前面 ProcessPostData 方法中標注出回發的值的差異。當有多個 changed 事件被觸發時,其先后順序是沒有保證的。
下面是 RaisePostBackEvent 方法。僅用于回發,而且是基類實現的私有方法。這是真正提交 form 的方法,除非是 postback。比如按鈕,或者其他通過 javascript 提交的控件被觸發。如果使用了 Validators, 如沒有手動調用 Validate 方法,這時也已經被調用了。有時候 ie 的 bug 會使得表單被提交,而不引發事件。
接著是 OnPreRender 方法。這通常是在被繪制到瀏覽器之前,要更改 page 及其子控件的最后機會。你也可以在這里創建動態控件。但這時只能捕獲 ViewState, 而不能接受 posted values, 而且沒有事件。因為上面提到的 ie 的 bug, 這里可以用來捕獲沒有觸發事件的 post back.
保存和繪制(Save and Render)
下一個是 SaveViewState 方法。不管是否 post back. 遞歸的應用到每一個子控件。ViewState 一般保存所有和 aspx 頁面里不一樣的屬性,不管是被代碼還是用戶更改的。注意,由于控件的值是通過他們在控件樹中的位置來保存的,所以如果在這之后添加動態控件到錯誤的位置, ViewState 可能會崩潰。
下面是 SavePageStateToPersistenceMedium 方法。它真正的保存 page 的 ViewState. 這個方法可覆蓋。如果重寫的話,注意這里由于 asp.net 的 bug, 需要手工設定一下 __VIEWSTATE,哪怕是空值。
接著是 Render 方法。它遞歸的調用到每個子控件,真正的繪制各自的 html, 發送到瀏覽器。在一些頁面模板方案中,常常在這里添加通用的 header 和 footer. 而不用使用服務器控件。 注意在這里能作的更改必須是純的 html. 因為這時候控件都已經繪制完了。
最后是 OnUnload 方法。它調用了 Dispose 方法。這個方法可以用來清理頁面中使用的非托管資源。特別是類似于關閉打開的文件或數據庫連接等。該方法只有當頁面已經被發送到客戶端瀏覽器后才發生。所以它只能對服務端的對象起作用。所以他不能在 page 的 trace 中被顯示。
上面就是 page 的生命循環。每次有一個新的請求時,以上過程就重復一次。
Listing 1: Page 的事件小結
Method PostBack Controls Constructor Always All AddParsedSubObject Always All DeterminePostBackMode Always Page OnInit Always All LoadPageStateFromPersistenceMedium PostBack Page LoadViewState PostBack All ProcessPostData1 PostBack Page OnLoad Always All ProcessPostData2 PostBack Page RaiseChangedEvents PostBack Page RaisePostBackEvent PostBack Page OnPreRender Always All SaveViewState Always All SavePageStateToPersistenceMedium Always Page Render Always All OnUnload Always All
文章列表
留言列表