ASP.NET頁面生存周期關(guān)鍵事件及執(zhí)行步驟
ASP.NET頁面生存周期介紹
一旦完全確定 HTTP 頁面處理程序類,ASP.NET 運(yùn)行時(shí)就調(diào)用該處理程序的 ProcessRequest 方法以處理請求。通常情況下,無需更改此方法的實(shí)現(xiàn)方式,因?yàn)樗怯?Page 類提供的。
此實(shí)現(xiàn)方法一開始就調(diào)用 FrameworkInitialize 方法,以此建立頁面的控件樹。此方法是 TemplateControl 類(Page 類本身就是從該類派生出來的)的一個(gè)受保護(hù)的虛擬成員。任何針對 .aspx 資源而動(dòng)態(tài)生成的處理程序都重寫 FrameworkInitialize。在此方法中,該頁面的完整控件樹得以構(gòu)建。
接下來,ProcessRequest 使該頁面經(jīng)歷若干階段:初始化,加載視圖狀態(tài)信息和回發(fā)數(shù)據(jù),加載頁面的用戶代碼并執(zhí)行回發(fā)服務(wù)器端事件。隨后,該頁面進(jìn)入呈現(xiàn)模式:收集更新后的視圖狀態(tài);生成 HTML 代碼然后將其發(fā)送到輸出控制臺。最后,卸載頁面,并認(rèn)為已完成對該請求的處理。
在各個(gè)階段中,頁面都會(huì)激發(fā)一些 Web 控件和用戶定義的代碼所能截獲并處理的事件。其中的一些事件是嵌入式控件專用的,因而并不能在 .aspx 代碼級進(jìn)行處理。
如果頁面想要處理某個(gè)事件,它應(yīng)該顯式地注冊相應(yīng)的處理程序。然而,為了向后兼容早期的 Visual Basic 編程風(fēng)格,ASP.NET 也支持一種隱式的事件掛起形式。在默認(rèn)情況下,頁面將嘗試把特定的方法名與事件匹配起來;如果找到匹配的方法,就認(rèn)為該方法是該事件的處理程序。ASP.NET 提供了六個(gè)方法名的特定識別。它們是 Page_Init、Page_Load、Page_DataBind、Page_PreRender 和 Page_Unload。這些方法被當(dāng)作是 Page 類所提供的相應(yīng)事件的處理程序。HTTP 運(yùn)行時(shí)將自動(dòng)把這些方法與頁面事件綁定起來,這樣一來,開發(fā)人員就不必編寫所需的粘接代碼。例如,名為 Page_Load 的方法與頁面的 Load 事件綁定,就像已編寫以下代碼一樣。
- this.Load += new EventHandler(this.Page_Load);
這種自動(dòng)識別特殊名稱的功能由 @Page 指令的 AutoEventWireup 屬性控制。如果將該屬性設(shè)置為 false,則任何想要處理某個(gè)事件的應(yīng)用程序都需顯式地連接到該頁面事件。如果頁面不使用自動(dòng)事件關(guān)聯(lián)功能,就不必進(jìn)行額外的操作以匹配各名稱和事件,從而其性能也稍有提升。應(yīng)該注意的是,所有的 Microsoft Visual Studio.NET 項(xiàng)目在創(chuàng)建時(shí)都禁用了 AutoEventWireup 屬性。然而,此屬性的默認(rèn)設(shè)置為 true,意味著諸如 Page_Load 等方法會(huì)被識別并被綁定到相關(guān)的事件。
頁面的執(zhí)行過程包括下面表格中所列的一系列階段,并以具有一些應(yīng)用程序級事件和/或受保護(hù)且可重寫的方法為特征。
表格 1. ASP.NET頁面生存周期中的關(guān)鍵事件
階段 |
頁面事件 |
可重寫方法 |
---|---|---|
頁面初始化 |
Init |
|
加載視圖狀態(tài) |
LoadViewState | |
處理回發(fā)數(shù)據(jù) |
實(shí)現(xiàn) IPostBackDataHandler 接口的任何控件中的 LoadPostData 方法 | |
加載頁面 |
Load |
|
回發(fā)更改通知 |
實(shí)現(xiàn) IPostBackDataHandler 接口的任何控件中的 RaisePostDataChangedEvent 方法 | |
處理回發(fā)事件 |
控件所定義的任何回發(fā)事件 |
實(shí)現(xiàn)了 IPostBackEventHandler 接口的任何控件的 RaisePostBackEvent 方法 |
頁面呈現(xiàn)前階段 |
PreRender |
|
保存視圖狀態(tài) |
SaveViewState | |
呈現(xiàn)頁面 |
Render | |
卸載頁面 |
Unload |
在頁面級上,以上所列的某些階段是不可見的,并僅影響服務(wù)器控件編寫者和那些湊巧要?jiǎng)?chuàng)建從 Page 派生的類的開發(fā)人員。頁面向外界發(fā)送的活動(dòng)信號僅包括 Init、Load、PreRender、Unload 以及嵌入式控件所定義的所有回發(fā)事件。
ASP.NET頁面生存周期:執(zhí)行的各個(gè)階段
頁面生存周期中的第一個(gè)階段是初始化。這一階段的標(biāo)志就是 Init 事件,在成功創(chuàng)建頁面的控件樹后,對應(yīng)用程序激發(fā)這個(gè)事件。換而言之,當(dāng) Init 事件發(fā)生時(shí),在 .aspx 源文件中靜態(tài)聲明的所有控件都已實(shí)例化并取其默認(rèn)值。控件可掛起 Init 事件,以便初始化在傳入的 Web 請求的生存周期中所需的任何設(shè)置。例如,此時(shí)控件可以加載外部模板文件或設(shè)置各個(gè)事件的處理程序。應(yīng)該注意到,這時(shí)還沒有視圖狀態(tài)信息可供使用。
在初始化之后,頁面框架立即加載該頁面的視圖狀態(tài)。所謂視圖狀態(tài)就是一些名稱/值對的集合,控件和頁面本身可將那些對所有 Web 請求都必須始終有效的任何信息存儲在其中。視圖狀態(tài)表示頁面的調(diào)用上下文。一般情況下,其中包含上次在服務(wù)器中處理該頁面時(shí)各控件的狀態(tài)。首次在會(huì)話中請求頁面時(shí),視圖狀態(tài)為空。在默認(rèn)情況下,視圖狀態(tài)被存儲在一個(gè)隱藏字段中,而該字段是自行添加到頁面中的。該字段名稱為 __VIEWSTATE。通過重寫 LoadViewState 方法(Control 類的一個(gè)受保護(hù)且可重寫的方法)組件開發(fā)人員可控制如何還原視圖狀態(tài)以及如何將其內(nèi)容映射到內(nèi)部狀態(tài)。
有些方法(如 LoadPageStateFromPersistenceMedium 及其相對的 SavePageStateToPersistenceMedium)可用于將視圖狀態(tài)加載并保存到別的存儲介質(zhì)(例如會(huì)話、數(shù)據(jù)庫或服務(wù)器端的文件)中。與 LoadViewState 不同,上述方法僅在派生自 Page 的各個(gè)類中才可使用。
一旦還原了視圖狀態(tài),頁面樹中的各個(gè)控件的狀態(tài)就與瀏覽器上次呈現(xiàn)該頁面時(shí)這些控件所處的狀態(tài)相同。下一步包括更新這些控件的狀態(tài)以加入客戶端的變更?;匕l(fā)數(shù)據(jù)處理階段使各個(gè)控件有機(jī)會(huì)更新其狀態(tài),以便準(zhǔn)確地反映相應(yīng)的 HTML 元素在客戶端的狀態(tài)。例如,一個(gè)服務(wù)器 TextBox 控件對應(yīng)的 HTML 元素是 <input type=text>。在回發(fā)數(shù)據(jù)階段,TextBox 控件將檢索 <input> 標(biāo)記的當(dāng)前值并用它刷新其內(nèi)部狀態(tài)。每個(gè)控件負(fù)責(zé)從已發(fā)送的數(shù)據(jù)中提取相應(yīng)值,并更新其某些屬性。TextBox 控件將更新其 Text 屬性,而 CheckBox 控件將刷新其 Checked 屬性。服務(wù)器控件和 HTML 元素之間的匹配關(guān)系由二者的 ID 確定。
在回發(fā)數(shù)據(jù)處理階段結(jié)束時(shí),頁面中的所有控件都根據(jù)客戶端上所輸入的更改來更新原先的狀態(tài)。此時(shí),對頁面激發(fā) Load 事件。
如果在處理兩個(gè)不同的請求時(shí)某個(gè)敏感的屬性被修改,則頁面上可能有些控件需要完成某些任務(wù)。例如,如果在客戶端修改了某個(gè)文本框控件的文本,則該控件激發(fā) TextChanged 事件。如果利用來自客戶端的值對該控件的一個(gè)或多個(gè)屬性進(jìn)行修改,每個(gè)控件可以決定激發(fā)一個(gè)適當(dāng)?shù)氖录?。對控件而言,如果這些更改是至關(guān)重要的,則這些控件實(shí)現(xiàn) IPostBackDataHandler 接口,在 Load 事件之后立即調(diào)用該接口的 LoadPostData 方法。通過編寫 LoadPostData 方法的代碼,一個(gè)控件可以確認(rèn)自最近一次請求以來是否發(fā)生了任何關(guān)鍵的更改,并激發(fā)自己的更改事件。
頁面生存周期內(nèi)的關(guān)鍵事件就是:它被調(diào)用來執(zhí)行與客戶端上所激發(fā)的某個(gè)事件相關(guān)聯(lián)的服務(wù)器端代碼。當(dāng)用戶單擊某個(gè)按鈕時(shí),頁面回發(fā)數(shù)據(jù)。已發(fā)送值的集合中包含該按鈕(該按鈕啟動(dòng)整個(gè)操作)的 ID。如果已知該控件實(shí)現(xiàn)了 IPostBackEventHandler 接口(按鈕和鏈接按鈕將實(shí)現(xiàn)此接口),則頁面框架調(diào)用 RaisePostBackEvent 方法。此方法所進(jìn)行的操作取決于相應(yīng)控件的類型。對于按鈕和鏈接按鈕,此方法查找 Click 事件處理程序并運(yùn)行相關(guān)的委托。
在處理回發(fā)事件后,頁面就準(zhǔn)備進(jìn)行呈現(xiàn)。這一階段的標(biāo)志是 PreRender 事件。各個(gè)控件可利用這個(gè)很好的時(shí)機(jī),以便執(zhí)行任何需要在保存視圖狀態(tài)和呈現(xiàn)輸出結(jié)果的前一刻完成的最后一些更新操作。下一個(gè)狀態(tài)為 SaveViewState,在這一狀態(tài)中所有控件以及頁面本身可以刷新自己的 ViewState 集合的內(nèi)容。所得到的視圖狀態(tài)隨后得以序列化、進(jìn)行哈希運(yùn)算、進(jìn)行 Base64 編碼并關(guān)聯(lián)到 __VIEWSTATE 隱藏字段。
通過重寫 Render 方法,即可更改各個(gè)控件的呈現(xiàn)機(jī)制。該方法獲取一個(gè) HTML 編寫器對象,并使用該對象聚集所有將針對該控件生成的 HTML 文本。Page 類的 Render 方法的默認(rèn)實(shí)現(xiàn)方式包括對所有成員控件的遞歸調(diào)用。對于每個(gè)控件,頁面都調(diào)用 Render 方法并將 HTML 輸出放入高速緩存。
一個(gè)頁面的最后生存標(biāo)志就是 Unload 事件,該事件在頁面對象被解除之前發(fā)生。在此事件中,您應(yīng)該釋放可能占用的任何關(guān)鍵資源(例如,文件、圖形對象、數(shù)據(jù)庫連接)。
終于,在此事件之后,瀏覽器收到 HTTP 響應(yīng)數(shù)據(jù)包并顯示頁面。
【編輯推薦】