本文內容 引入 ASP.NET 頁生命周期概述 常規頁生命周期階段 生命周期事件 其他頁生命周期註意事項 數據綁定控制項的數據綁定事件 引入 工作之初,我用 VS 開發 Web 應用程式,最常用的頁面事件是 Page_Load 和 Page_Init,它們處於不同的頁面生命期。但漸漸地,這兩個事件已經 ...
本文內容
- 引入
- ASP.NET 頁生命周期概述
- 常規頁生命周期階段
- 生命周期事件
- 其他頁生命周期註意事項
- 數據綁定控制項的數據綁定事件
引入
工作之初,我用 VS 開發 Web 應用程式,最常用的頁面事件是 Page_Load 和 Page_Init,它們處於不同的頁面生命期。但漸漸地,這兩個事件已經不大夠用了。開始使用 PreRender 事件,希望在頁面呈現前,做最後的修改,接下來用 RowDataBound,讓GridView 控制項在綁定數據行時,可以改變行的式樣……當自己嘗試封裝 ASP.NET 控制項,比如分頁控制項,那以上事件就完全不夠。
若更好地開發 Web 應用程式,或是使用三方組件,比如 Ext.Net、ComponentArt,不能忽視頁面生命期。
本文內容來自 MSDN,有點刪減,讓它更通俗一點。雖然本文全是文字,有點枯燥,但對於提高自己,以及在實際項目中瞭解三方框架很有幫助。
ASP.NET 頁生命周期概述
ASP.NET 頁運行時,此頁將經歷一個生命周期,在生命周期中將執行一系列處理步驟。這些步驟包括初始化、實例化控制項、還原和維護狀態、運行事件處理程式代碼以及呈現。
瞭解頁生命周期的重要性在於,在適當的生命周期階段編寫代碼,以達到預期效果。
此外,如果要開發自定義控制項,就必須熟悉頁生命周期,以便正確初始化控制項,使用視圖數據(通常是視圖變數)填充控制項屬性,以及運行控制項的任何行為代碼。
常規頁生命周期階段
一般來說,頁經歷以下幾個階段。除了頁生命周期階段以外,在請求前後還存在“應用程式階段”,但這些階段並不特定於頁。
階段 | 說明 |
頁請求 | 頁請求發生在頁生命周期開始之前。用戶請求頁時,ASP.NET 將確定是否需要分析和編譯頁(從而開始頁的生命周期),或者是否可以在不運行頁的情況下發送頁的緩存版本以進行響應。 |
開始 | 在開始階段,將設置頁屬性,如 Request 和 Response。在此階段,頁還將確定請求是回發的請求,還是新的請求,並設置 IsPostBack 屬性。此外,在開始階段期間,還將設置頁的UICulture 屬性。 |
頁初始化 | 頁初始化期間,可以使用頁中的控制項,並將設置每個控制項的 UniqueID 屬性。此外,主題都將應用於頁。如果當前請求是回發的請求,則回發數據尚未載入,並且控制項屬性值尚未還原為視圖狀態中的值。 |
載入 | 載入期間,如果當前請求是回發的請求,則將使用從視圖狀態和控制項狀態恢復的信息載入控制項屬性。 |
驗證 | 在驗證期間,將調用所有驗證程式控制項的 Validate 方法,此方法將設置各個驗證程式控制項和頁的 IsValid 屬性。 |
回發事件處理 | 如果請求是回發的請求,則將調用所有事件處理程式。 |
呈現 | 在呈現之前,會針對該頁和所有控制項保存視圖狀態。在呈現階段中,頁會針對每個控制項調用 Render 方法,它會提供一個文本編寫器,用於將控制項的輸出寫入頁的 Response 屬性的 OutputStream 中。 |
卸載 | 完全呈現頁並,已將頁發送至客戶端、準備丟棄該頁後,將調用卸載。此時,將卸載頁屬性(如 Response 和 Request),並執行清理。 |
生命周期事件
對於控制項事件,通過以聲明方式(如 onclick),或以編程方式,將事件處理程式綁定到事件。
頁支持自動事件。ASP.NET 將查找具有特定名稱的方法,併在引發了特定事件時自動運行這些方法。若 @ Page 指令的 AutoEventWireup 屬性為 true(預設為 true),則頁事件將自動綁定到 Page_*** 命名的方法,如 Page_Load 和 Page_Init。
下表列出了最常用的頁生命周期事件(當然還有其他,但大多數頁處理方案不使用,而主要由伺服器控制項使用,以初始化和呈現它們自己)。
頁事件 | 典型使用 |
PreInit |
在頁初始化開始時發生。 該事件執行以下操作:
說明: 如果請求是回發請求,則控制項的值尚未從視圖狀態還原。若在此階段設置控制項屬性,則其值可能會在下一事件中被覆蓋。 備註: 該事件是在頁生命周期的早期階段中可以訪問的事件。在 PreInit 事件後,將載入個性化信息和頁主題(如果有)。 |
Init |
當伺服器控制項初始化時發生。初始化是控制項生存期的第一步。 在所有控制項都已初始化,且已應用所有外觀設置後引發。使用該事件來讀取或初始化控制項屬性。 備註: 伺服器控制項應執行任何創建和設置實例所需的初始化步驟。在該事件內無法使用視圖狀態信息(尚未填充)。在該事件的生存期內不應訪問其他伺服器控制項,不論它是此控制項的子級還是父級。不一定會創建其他伺服器控制項,也不一定能夠訪問它們。 |
InitComplete |
在頁初始化完成時發生。 由 Page 對象引發。該事件處理要求是先完成所有初始化工作的任務。 備註: InitComplete 事件在頁的初始化階段結束時調用。在頁生命周期的此階段中,頁中聲明的所有控制項都已初始化,但頁的狀態尚未填充。您可以訪問伺服器控制項,但其中將不包含從用戶返回的信息。 ComponentArt 組件使用該事件創建控制項。 |
PreLoad |
在頁 Load 事件之前發生。 在 Page 引發該事件後,它會為自身和所有控制項載入視圖狀態,處理 Request 實例包括的任何回發數據。 備註: PreLoad 事件在所有回發數據處理之後但在 Load 事件之前引發。在 OnLoadComplete 事件之前,還會再進行一次載入回發數據的嘗試。 |
Load |
當伺服器控制項載入到 Page 對象中時發生。 在 Page 上調用 OnLoad 事件方法,以遞歸方式對每個子控制項執行相同操作,如此迴圈,直到載入完本頁和所有控制項為止。 使用 OnLoad 事件方法來設置控制項中的屬性並建立資料庫連接。 備註: 通知伺服器控制項執行任何設置為在每次頁請求時發生的處理步驟。可以訪問該事件的視圖狀態信息和 Web 窗體 POST 數據。還可以訪問頁控制項內的其他伺服器控制項。 |
控制項事件 |
使用這些事件來處理特定的控制項事件,如 Button 控制項的 Click 事件或 TextBox 控制項的 TextChanged 事件。 說明: 在回發請求中,如果頁包含驗證程式控制項,請在執行任何處理之前檢查 Page 和各個驗證控制項的 IsValid 屬性。 |
LoadComplete |
在頁生命周期的載入階段結束時發生。 對需要載入頁上的所有控制項使用該事件。 備註: LoadComplete 事件在所有回發數據和視圖狀態數據都載入到頁和頁上的所有控制項中後發生。為了將視圖狀態用於動態添加的控制項,必須在頁生命周期的預呈現階段中或該階段之前添加這些控制項。 |
PreRender |
在載入 Control 對象之後、呈現之前發生。 在該事件發生前:
備註: 使用該事件在伺服器控制項呈現給頁之前執行任何更新。在該事件的生存期內可以保存伺服器控制項視圖狀態的任何更改。不保存呈現階段內所做的同樣更改。 |
SaveStateComplete |
在頁已完成對頁和其控制項的所有視圖狀態和控制項狀態信息的保存後發生。 在該事件發生前,已針對頁和所有控制項保存了 ViewState。將忽略此時對頁或控制項進行的任何更改。 使用該事件執行滿足以下條件的任務:要求已經保存了視圖狀態,但未對控制項進行任何更改。 備註: 網頁上控制項的狀態信息是在 PreRenderComplete 事件後保存的。SaveStateComplete 事件在將頁和其控制項的視圖狀態和控制項狀態保存到持久化之後引發。 這是在頁被呈現到請求瀏覽器之前引發的最後一個事件。 |
Render |
將伺服器控制項內容發送給 HtmlTextWriter 對象,此對象編寫將在客戶端呈現的內容。 Render不是事件。在這個階段,Page 對象會在每個控制項上調用此方法。所有伺服器控制項都有一個用於寫出發送給瀏覽器的控制項標記的 Render 方法。 如果創建自定義控制項,通常要覆蓋此方法以輸出控制項的標記。不過,如果自定義控制項只合併標準的 ASP.NET Web 伺服器控制項,不合併自定義標記,則不需要覆蓋 Render方法。 用戶控制項(.ascx 文件)自動合併呈現,因此不需要在代碼中顯式呈現該控制項。 備註: 在開發自定義伺服器控制項時,可以重寫此方法以生成 ASP.NET 頁的內容。 |
Unload |
當伺服器控制項從記憶體中卸載時發生。 該事件首先針對每個控制項發生,繼而針對該頁發生。在控制項中,使用該事件對特定控制項執行最後清理,如關閉控制項特定資料庫連接。 對於頁自己,使用該事件來執行最後清理工作,如:關閉打開的文件和資料庫連接,或完成日誌記錄或其他請求的特定任務。 說明: 在卸載階段,頁及其控制項已被呈現,因此無法對響應流做進一步更改。如果嘗試調用方法(如 Response.Write 方法),則該頁將引發異常。 備註: 在卸載實例前,伺服器控制項必須在控制項生命期的此階段執行所有最後的清理操作,如關閉文件、關閉資料庫連接和丟棄對象。 |
其他頁生命周期註意事項
各個伺服器控制項都有自己的生命周期,該生命周期與頁的類似。例如,控制項的 Init 和 Load 事件在相應的頁事件期間發生。
雖然 Init 和 Load 都在每個控制項上以遞歸方式發生,但它們的發生順序相反。每個子控制項的 Init 事件(還有 Unload 事件)在為其容器引發相應的事件之前發生(由下到上)。但是,容器的Load 事件是在其子控制項的 Load 事件之前發生(由上到下)。
可以通過處理控制項的事件(如 Button 控制項的 Click 事件和 ListBox 控制項的 SelectedIndexChanged 事件)來自定義控制項的外觀或內容。在某些情況下,可能也需處理控制項的 DataBinding 或DataBound 事件。
當從 Page 類繼承時,除了可以處理由頁引發的事件外,還可以覆蓋頁基類中的方法。例如,可以覆蓋頁的 InitializeCulture 方法,以便動態設置區域性信息。
添加控制項的追趕事件
如果控制項是在運行時動態創建的,或以聲明方式在數據綁定控制項的模板中創建,那麼它們的事件最初與頁上的其他控制項的事件並不同步。例如,對於運行時添加的控制項,Init 和 Load 事件在頁生命周期中的發生時間可能要比以聲明方式創建的控制項的相同事件晚得多。因此,從實例化那一刻起,動態添加的控制項的事件就一直是在模板中的控制項的事件之後發生,直到趕上該控制項加入Controls 集合時所對應事件為止。
一般來說,除非存在嵌套數據綁定控制項,否則,不必擔心這種情況。如果子控制項已執行數據綁定,但其容器控制項尚未執行數據綁定,則子控制項中的數據與其容器控制項中的數據可能不同步。如果子控制項中的數據根據容器控制項中的數據綁定值執行了處理,這種情況則尤其顯著。
例如,假定有一個 GridView,它的每一行顯示一條公司記錄,還有一個 ListBox 控制項包含公司管理者的列表。若要填充管理者列表,則需要將 ListBox 控制項綁定到一個數據源控制項(如SqlDataSource),後者在查詢中使用 CompanyID 來檢索公司管理者數據。
如果以聲明方式設置了 ListBox 控制項的數據綁定屬性(如 DataSourceID 和 DataMember),ListBox 控制項將嘗試在包含行的 DataBinding 事件期間綁定到其數據源。不過,行的 CompanyID 欄位直到 GridView 控制項的 RowDataBound 事件發生後才包含值。這種情況下,先綁定子控制項(ListBox 控制項),後綁定包含控制項(GridView 控制項),因此它們的數據綁定階段並不同步。
若要避免此種情況,需要將 ListBox 控制項的數據源控制項與 ListBox 控制項自身放在同一模板項中,並且不要以聲明方式設置 ListBox 的數據綁定屬性。而應在 RowDataBound 事件期間在運行時以編程方式設置它們,這樣,到 CompanyID 信息可用時 ListBox 控制項才會綁定到其數據。
數據綁定控制項的數據綁定事件
為了理解頁生命周期與數據綁定事件之間的關係,下表列出了數據綁定控制項(如 GridView、DetailsView 和 FormView 控制項)中與數據相關的事件。
控制項事件 | 典型使用 |
DataBinding |
該事件在包含控制項(或 Page 對象)的 PreRender 事件之前由數據綁定控制項引發,會標記控制項到數據的綁定過程的起點。 如果需要,使用該事件以手動方式打開資料庫連接。(數據源控制項通常不需要如此操作。) |
RowCreated (僅限 GridView)或 ItemCreated(DataList、DetailsView、SiteMapPath、DataGrid、FormView、Repeater和 ListView 控制項) |
使用該事件來操作不依賴於數據綁定的內容。例如,在運行時,可以以編程方式向 GridView 控制項中的頁眉或頁腳行添加格式。 |
RowDataBound (僅限 GridView)或ItemDataBound(DataList、SiteMapPath、DataGrid、Repeater 和 ListView 控制項) | 當該事件發生時,行或項中的數據可用,因此,可以在子數據源控制項上格式化數據或設置FilterExpression 屬性,以便顯示行或項中的相關數據。 |
DataBound |
該事件在數據綁定控制項中標記數據綁定操作的結尾。在 GridView 控制項中,會針對所有行和任何子控制項完成數據綁定。 使用該事件格式化數據綁定內容,或在依賴來自當前控制項的內容的值的其他控制項中啟動數據綁定。 |