一、ASP.NET 母版頁(yè)和內(nèi)容頁(yè)中的事件
母版頁(yè)和內(nèi)容頁(yè)都可以包含控件的事件處理程序。對(duì)于控件而言,事件是在本地處理的,即內(nèi)容頁(yè)中的控件在內(nèi)容頁(yè)中引發(fā)事件,母版頁(yè)中的控件在母版頁(yè)中引發(fā)事件。控件事件不會(huì)從內(nèi)容頁(yè)發(fā)送到母版頁(yè)。同樣,也不能在內(nèi)容頁(yè)中處理來(lái)自母版頁(yè)控件的事件。
在某些情況下,內(nèi)容頁(yè)和母版頁(yè)中會(huì)引發(fā)相同的事件。例如,兩者都引發(fā) Init 和 Load 事件。引發(fā)事件的一般規(guī)則是初始化事件從最里面的控件向最外面的控件引發(fā),所有其他事件則從最外面的控件向最里面的控件引發(fā)。
請(qǐng)記住,母版頁(yè)會(huì)合并到內(nèi)容頁(yè)中并被視為內(nèi)容頁(yè)中的一個(gè)控件,這一點(diǎn)十分有用。
下面是母版頁(yè)與內(nèi)容頁(yè)合并后事件的發(fā)生順序:
母版頁(yè)和內(nèi)容頁(yè)中的事件順序?qū)τ陧?yè)面開(kāi)發(fā)人員并不重要。但是,如果您創(chuàng)建的事件處理程序取決于某些事件的可用性,那么您將發(fā)現(xiàn),了解母版頁(yè)和內(nèi)容頁(yè)中的事件順序很有幫助。
二、關(guān)于asp.net中頁(yè)面事件加載的先后順序
1、Page 執(zhí)行中將按照如下順序激活事件:
Page.PreInit
Page.Init
Page.InitComplite
Page.PreLoad
Page.Load
Page.LoadComplete
Page.PreRender
Page.PreRenderComplete
2、如果頁(yè)面從另一個(gè)頁(yè)面繼承,如BasePage:System.Web.UI.Page,在BasePage中做了一些擴(kuò)展,如權(quán)限檢查,而其他頁(yè)面從BasePage繼承,
則BasePage和最終Page的事件激活順序是:
UI.PreInit
Page.PreInit
UI.Init
Page.Init
UI.InitComplite
Page.InitComplite
UI.PreLoad
Page.PreLoad
UI.Load
Page.Load
UI.LoadComplete
Page.LoadComplete
UI.PreRender
Page.PreRender
UI.PreRenderComplete
Page.PreRenderComplete
3、如果使用了MasterPage,則MasterPage中的事件和ContentPage中的事件按照下面順序激活:
ContentPage.PreInit
Master.Init
ContentPage.Init
ContentPage.InitComplite
ContentPage.PreLoad
ContentPage.Load
Master.Load
ContentPage.LoadComplete
ContentPage.PreRender
Master.PreRender
ContentPage.PreRenderComplete
4、更進(jìn)一步,如果ContentPage繼承BasePage,那么,各事件的執(zhí)行順序?qū)⒆兂桑?/STRONG>
UI.PreInit
ContentPage.PreInit
Master.Init
UI.Init
ContentPage.Init
UI.InitComplite
ContentPage.InitComplite
UI.PreLoad
ContentPage.PreLoad
UI.Load
ContentPage.Load
Master.Load
UI.LoadComplete
ContentPage.LoadComplete
UI.PreRender
ContentPage.PreRender
Master.PreRender
UI.PreRenderComplete
ContentPage.PreRenderComplete
這讓我知道了他們有繼承時(shí)加載的順序。
即:先加載繼承頁(yè)的,再加載自己的,如果繼承頁(yè)有繼承則先加載繼承頁(yè)的繼承。
其實(shí)是個(gè)很簡(jiǎn)單的內(nèi)容。順便寫下Page事件:
事件處理器名稱 |
發(fā)生時(shí)間 |
Page_Init |
在Web窗體的視圖狀態(tài)加載服務(wù)器控件并對(duì)其初始化。 這是web窗體生命周期的第一步 |
Page_Load |
在Page對(duì)象上載入服務(wù)器控件。由于此時(shí)視圖狀態(tài)信息是可以使用的, 因此載這里可以用代碼來(lái)改變空間的設(shè)置或者載頁(yè)面上顯示文本。 |
Page_PreRender |
應(yīng)用程序?qū)⒁尸F(xiàn)Page對(duì)象 |
Page_Unload |
頁(yè)面從內(nèi)存中卸載 |
Page_Error |
發(fā)生未處理的異常 |
Page_AbortTransaction |
事務(wù)處理被終止 |
Page_CommitTransaction |
事務(wù)處理被接受 |
Page_DataBinding |
把頁(yè)面上的服務(wù)器空間和數(shù)據(jù)源綁定載一起 |
Page_Disposed |
Page對(duì)象從內(nèi)存中釋放掉。這是Page對(duì)象生命周期中的最后一個(gè)事件 |
Init,Load,PreRender事件執(zhí)行順序:
1)控件的Init事件
2)控件所在頁(yè)面的Init事件
3)控件所在頁(yè)面的Load事件
4)控件的Load事件
5)控件所在頁(yè)面的PreRender事件
6)控件的PreRender事件
規(guī)律:
1)Init事件從最里面的控件(包括用戶控件及普通控件)向最外面的控件(頁(yè)面)引發(fā),Load及PreRender等其他事件從最外面的控件向最里面的控件引發(fā);
2)控件之間相同事件的執(zhí)行順序依控件在頁(yè)面的位置按從左到右,從上到下的先后順序執(zhí)行。
注意:
1)切記用戶控件也被視為頁(yè)面中的一個(gè)控件;
2)把用戶控件作為單獨(dú)的一個(gè)特殊頁(yè)面來(lái)看,它本身及其所包含的控件同樣遵守相同的規(guī)律;
3)有時(shí)在客戶端程序(如javascript)中會(huì)用到客戶端body對(duì)像的onload事件,注意這個(gè)客戶端事件是最后執(zhí)行,即在服務(wù)器端所有事件執(zhí)行完后才執(zhí)行。
==============================================================
轉(zhuǎn)載一篇關(guān)于頁(yè)面對(duì)象模型的文章,說(shuō)得比較詳細(xì),有助理解。沒(méi)事的時(shí)候就多看兩遍,慢慢體會(huì):)。
對(duì)由 Microsoft® Internet 信息服務(wù) (IIS) 處理的 Microsoft® ASP.NET 頁(yè)面的每個(gè)請(qǐng)求都會(huì)被移交到 ASP.NET HTTP 管道。HTTP 管道由一系列托管對(duì)象組成,這些托管對(duì)象按順序處理請(qǐng)求,并將 URL 轉(zhuǎn)換為純 HTML 文本。HTTP 管道的入口是 HttpRuntime 類。ASP.NET 結(jié)構(gòu)為輔助進(jìn)程中的每個(gè) AppDomain 創(chuàng)建一個(gè)此類的實(shí)例。(請(qǐng)注意,輔助進(jìn)程為每個(gè)當(dāng)前正在運(yùn)行的 ASP.NET 應(yīng)用程序維護(hù)一個(gè)特定的 AppDomain。)
HttpRuntime 類從內(nèi)部池中獲取 HttpApplication 對(duì)象,并安排此對(duì)象來(lái)處理請(qǐng)求。HTTP 應(yīng)用程序管理器完成的主要任務(wù)就是找到將真正處理請(qǐng)求的類。當(dāng)請(qǐng)求 .aspx 資源時(shí),處理程序就是頁(yè)面處理程序,即從 Page 繼承的類的實(shí)例。資源類型和處理程序類型之間的關(guān)聯(lián)關(guān)系存儲(chǔ)在應(yīng)用程序的配置文件中。更確切地說(shuō),默認(rèn)的映射集是在 machine.config 文件的 <httpHandlers> 部分定義的。但是,應(yīng)用程序可以在本地的 web.config 文件中自定義自己的 HTTP 處理程序列表。以下這一行代碼就是用來(lái)為 .aspx 資源定義 HTTP 處理程序的。
<add verb="*" path="*.aspx" type="System.Web.UI.PageHandlerFactory"/>
擴(kuò)展名可以與處理程序類相關(guān)聯(lián),并且更多是與處理程序工廠類相關(guān)聯(lián)。在所有情況下,負(fù)責(zé)處理請(qǐng)求的 HttpApplication 對(duì)象都會(huì)獲得一個(gè)實(shí)現(xiàn) IHttpHandler 接口的對(duì)象。如果根據(jù) HTTP 處理程序來(lái)解析關(guān)聯(lián)的資源/類,則返回的類將直接實(shí)現(xiàn)接口。如果資源被綁定到處理程序工廠,則還需要額外的步驟。處理程序工廠類實(shí)現(xiàn) IHttpHandlerFactory 接口,此接口的 GetHandler 方法將返回一個(gè)基于 IHttpHandler 的對(duì)象。
HTTP 運(yùn)行時(shí)是如何結(jié)束這個(gè)循環(huán)并處理頁(yè)面請(qǐng)求的?ProcessRequest 方法在 IHttpHandler 接口中非常重要。通過(guò)對(duì)代表被請(qǐng)求頁(yè)面的對(duì)象調(diào)用此方法,ASP.NET 結(jié)構(gòu)會(huì)啟動(dòng)將生成瀏覽器輸出的進(jìn)程。
真正的 Page 類特定頁(yè)面的 HTTP 處理程序類型取決于 URL。首次調(diào)用 URL 時(shí),將構(gòu)建一個(gè)新的類,這個(gè)類被動(dòng)態(tài)編譯為一個(gè)程序集。檢查 .aspx 資源的分析進(jìn)程的結(jié)果是類的源代碼。該類被定義為命名空間 ASP 的組成部分,并且被賦予了一個(gè)模擬原始 URL 的名稱。例如,如果 URL 的終點(diǎn)是 page.aspx,則類的名稱就是 ASP.Page_aspx。不過(guò),類的名稱可以通過(guò)編程方式來(lái)控制,方法是在 @Page 指令中設(shè)置 ClassName 屬性。
HTTP 處理程序的基類是 Page。這個(gè)類定義了由所有頁(yè)面處理程序共享的方法和屬性的最小集合。Page 類實(shí)現(xiàn) IHttpHandler 接口。
在很多情況下,實(shí)際處理程序的基類并不是 Page,而是其他的類。例如,如果使用了代碼分離,就會(huì)出現(xiàn)這種情況。代碼分離是一項(xiàng)開(kāi)發(fā)技術(shù),它可以將頁(yè)面所需的代碼隔離到單獨(dú)的 C# 和 Microsoft Visual Basic® .NET 類中。頁(yè)面的代碼是一組事件處理程序和輔助方法,這些處理程序和方法真正決定了頁(yè)面的行為??梢允褂?<script runat=server> 標(biāo)記對(duì)此代碼進(jìn)行內(nèi)聯(lián)定義,或者將其放置在外部類(代碼分離類)中。代碼分離類是從 Page 繼承并使用額外的方法的類,被指定用作 HTTP 處理程序的基類。
還有一種情況,HTTP 處理程序也不是基于 Page 的,即在應(yīng)用程序配置文件的 <pages> 部分中,包含了 PageBaseType 屬性的重新定義。
<pages PageBaseType="Classes.MyPage, mypage" />
PageBaseType 屬性指明包含頁(yè)面處理程序的基類的類型和程序集。從 Page 導(dǎo)出的這個(gè)類可以自動(dòng)賦予處理程序擴(kuò)展的自定義方法和屬性集。
頁(yè)面的生命周期完全識(shí)別 HTTP 頁(yè)面處理程序類后,ASP.NET 運(yùn)行時(shí)將調(diào)用處理程序的 ProcessRequest 方法來(lái)處理請(qǐng)求。通常情況下,無(wú)需更改此方法的實(shí)現(xiàn),因?yàn)樗怯?Page 類提供的。
此實(shí)現(xiàn)將從調(diào)用為頁(yè)面構(gòu)建控件樹(shù)的 FrameworkInitialize 方法開(kāi)始。FrameworkInitialize 方法是 TemplateControl 類(Page 本身從此類導(dǎo)出)的一個(gè)受保護(hù)的虛擬成員。所有為 .aspx 資源動(dòng)態(tài)生成的處理程序都將覆蓋 FrameworkInitialize。在此方法中,構(gòu)建了頁(yè)面的整個(gè)控件樹(shù)。
接下來(lái),ProcessRequest 使頁(yè)面經(jīng)歷了各個(gè)階段:初始化、加載視圖狀態(tài)信息和回發(fā)數(shù)據(jù)、加載頁(yè)面的用戶代碼以及執(zhí)行回發(fā)服務(wù)器端事件。之后,頁(yè)面進(jìn)入顯示模式:收集更新的視圖狀態(tài),生成 HTML 代碼并隨后將代碼發(fā)送到輸出控制臺(tái)。最后,卸載頁(yè)面,并認(rèn)為請(qǐng)求處理完畢。
在各個(gè)階段中,頁(yè)面會(huì)觸發(fā)少數(shù)幾個(gè)事件,這些事件可以由 Web 控件和用戶定義的代碼截取并進(jìn)行處理。其中的一些事件是嵌入式控件專用的,因此無(wú)法在 .aspx 代碼級(jí)進(jìn)行處理。
要處理特定事件的頁(yè)面應(yīng)該明確注冊(cè)一個(gè)適合的處理程序。不過(guò),為了向后兼容早期的 Visual Basic 編程風(fēng)格,ASP.NET 也支持隱式事件掛鉤的形式。默認(rèn)情況下,頁(yè)面會(huì)嘗試將特定的方法名稱與事件相匹配,如果實(shí)現(xiàn)匹配,則認(rèn)為此方法就是匹配事件的處理程序。ASP.NET 提供了六種方法名稱的特定識(shí)別,它們是 Page_Init、Page_Load、Page_DataBind、Page_PreRender 和 Page_Unload。這些方法被認(rèn)為是由 Page 類提供的相應(yīng)事件的處理程序。HTTP 運(yùn)行時(shí)會(huì)自動(dòng)將這些方法綁定到頁(yè)面事件,這樣,開(kāi)發(fā)人員就不必再編寫所需的粘接代碼了。例如,如果命名為 Page_Load 的方法綁定到頁(yè)面的 Load 事件,則可省去以下代碼。
this.Load += new EventHandler(this.Page_Load);
對(duì)特定名稱的自動(dòng)識(shí)別是由 @Page 指令的 AutoEventWireup 屬性控制的。如果該屬性設(shè)置為 false,則要處理事件的所有應(yīng)用程序都需要明確連接到頁(yè)面事件。不使用自動(dòng)綁定事件的頁(yè)面性能會(huì)稍好一些,因?yàn)椴恍枰~外匹配名稱與事件。請(qǐng)注意,所有 Microsoft Visual Studio® .NET 項(xiàng)目都是在禁用 AutoEventWireup 屬性的情況下創(chuàng)建的。但是,該屬性的默認(rèn)設(shè)置是 true,即 Page_Load 等方法會(huì)被識(shí)別,并被綁定到相關(guān)聯(lián)的事件。
下表中按順序列出了頁(yè)面的執(zhí)行包括的幾個(gè)階段,執(zhí)行的標(biāo)志是一些應(yīng)用程序級(jí)的事件和/或受保護(hù)并可覆蓋的方法。
表 1:ASP.NET 頁(yè)面生命中的關(guān)鍵事件
階段 | 頁(yè)面事件 | 可覆蓋的方法 |
---|---|---|
頁(yè)面初始化 | Init | |
加載視圖狀態(tài) | LoadViewState | |
處理回發(fā)數(shù)據(jù) | 任意實(shí)現(xiàn) IPostBackDataHandler 接口的控件中的 LoadPostData 方法 | |
加載頁(yè)面 | Load | |
回發(fā)更改通知 | 任意實(shí)現(xiàn) IPostBackDataHandler 接口的控件中的 RaisePostDataChangedEvent 方法 | |
處理回發(fā)事件 | 由控件定義的任意回發(fā)事件 | 任意實(shí)現(xiàn) IPostBackDataHandler 接口的控件中的 RaisePostBackEvent 方法 |
頁(yè)面顯示前階段 | PreRender | |
保存視圖狀態(tài) | SaveViewState | |
顯示頁(yè)面 | Render | |
卸載頁(yè)面 | Unload |
以上所列的階段中有些在頁(yè)面級(jí)是不可見(jiàn)的,并且僅對(duì)服務(wù)器控件的編寫者和要?jiǎng)?chuàng)建從 Page 導(dǎo)出的類的開(kāi)發(fā)人員有意義。Init、Load、PreRender、Unload,再加上由嵌入式控件定義的所有回發(fā)事件,就構(gòu)成了向外發(fā)送頁(yè)面的各個(gè)階段標(biāo)記。
執(zhí)行的各個(gè)階段頁(yè)面生命周期中的第一個(gè)階段是初始化。這個(gè)階段的標(biāo)志是 Init 事件。在成功創(chuàng)建頁(yè)面的控件樹(shù)后,將對(duì)應(yīng)用程序觸發(fā)此事件。換句話說(shuō),當(dāng) Init 事件發(fā)生時(shí),.aspx 源文件中靜態(tài)聲明的所有控件都已實(shí)例化并采用各自的默認(rèn)值。控件可以截取 Init 事件以初始化在傳入的 Web 請(qǐng)求的生命周期內(nèi)所需的所有設(shè)置。例如,這時(shí)控件可以加載外部模板文件或設(shè)置事件的處理程序。請(qǐng)注意,這時(shí)視圖狀態(tài)信息尚不可用。
初始化之后,頁(yè)面框架將加載頁(yè)面的視圖狀態(tài)。視圖狀態(tài)是名稱/值對(duì)的集合,在此集合中,控件和頁(yè)面本身存儲(chǔ)了對(duì)所有 Web 請(qǐng)求都必須始終有效的全部信息。視圖狀態(tài)代表了頁(yè)面的調(diào)用上下文。通常,它包含上次在服務(wù)器上處理頁(yè)面時(shí)控件的狀態(tài)。首次在會(huì)話中請(qǐng)求頁(yè)面時(shí),視圖狀態(tài)為空。默認(rèn)情況下,視圖狀態(tài)存儲(chǔ)在靜默添加到頁(yè)面的隱藏字段中,該字段的名稱是 __VIEWSTATE。通過(guò)覆蓋 LoadViewState 方法(Control 類的受保護(hù)、可覆蓋方法),組件開(kāi)發(fā)人員可以控制視圖狀態(tài)的存儲(chǔ)方式以及視圖狀態(tài)的內(nèi)容映射到內(nèi)部狀態(tài)的方式。
有些方法(如 LoadPageStateFromPersistenceMedium 以及其對(duì)應(yīng)的 SavePageStateToPersistenceMedium),可以用來(lái)將視圖狀態(tài)加載并保存到其他存儲(chǔ)介質(zhì)(例如會(huì)話、數(shù)據(jù)庫(kù)或服務(wù)器端文件)中。與 LoadViewState 不同,上述方法只能在從 Page 導(dǎo)出的類中使用。
存儲(chǔ)視圖狀態(tài)之后,頁(yè)面樹(shù)中控件的狀態(tài)與頁(yè)面最后一次顯示在瀏覽器中的狀態(tài)相同。下一步是更新它們的狀態(tài)以加入客戶端的更改。處理回發(fā)數(shù)據(jù)階段使控件有機(jī)會(huì)更新其狀態(tài),從而準(zhǔn)確反映客戶端相應(yīng)的 HTML 元素的狀態(tài)。例如,服務(wù)器的 TextBox 控件對(duì)應(yīng)的 HTML 元素是 <input type=text>。在回發(fā)數(shù)據(jù)階段,TextBox 控件將檢索 <input> 標(biāo)記的當(dāng)前值,并使用該值來(lái)刷新自己內(nèi)部的狀態(tài)。每個(gè)控件都要從回發(fā)的數(shù)據(jù)中提取值并更新自己的部分屬性。TextBox 控件將更新它的 Text 屬性,而 CheckBox 控件將刷新它的 Checked 屬性。服務(wù)器控件和 HTML 元素的對(duì)應(yīng)關(guān)系可以通過(guò)二者的 ID 找到。
在處理回發(fā)數(shù)據(jù)階段的最后,頁(yè)面中的所有控件的狀態(tài)都將使用客戶端輸入的更改來(lái)更新前一狀態(tài)。這時(shí),將對(duì)頁(yè)面觸發(fā) Load 事件。
頁(yè)面中可能會(huì)有一些控件,當(dāng)其某個(gè)敏感屬性在兩個(gè)不同的請(qǐng)求中被修改時(shí),需要完成特定的任務(wù)。例如,如果 TextBox 控件的文本在客戶端被修改,則此控件將觸發(fā) TextChanged 事件。每個(gè)控件在其一個(gè)或多個(gè)屬性被修改為客戶端輸入的值時(shí)都可以決定觸發(fā)相應(yīng)的事件。對(duì)于這些更改對(duì)其非常關(guān)鍵的控件,控件實(shí)現(xiàn) IPostBackDataHandler 接口,此接口的 LoadPostData 方法是在 Load 事件后立即調(diào)用的。通過(guò)對(duì) LoadPostData 方法進(jìn)行編碼,控件將驗(yàn)證自上次請(qǐng)求后是否發(fā)生了關(guān)鍵更改,并觸發(fā)自己的更改事件。
頁(yè)面生命周期中的關(guān)鍵事件是被調(diào)用以執(zhí)行服務(wù)器端代碼的事件,此代碼與客戶端觸發(fā)的事件相關(guān)聯(lián)。當(dāng)用戶單擊按鈕時(shí),將回發(fā)頁(yè)面?;匕l(fā)值的集合中包括啟動(dòng)整個(gè)操作的按鈕的 ID。如果控件實(shí)現(xiàn) IPostBackEventHandler 接口(如按鈕和鏈接按鈕),頁(yè)面框架將調(diào)用 RaisePostBackEvent 方法。此方法的行為取決于控件的類型。就按鈕和鏈接按鈕而言,此方法將查找 Click 事件處理程序并運(yùn)行相關(guān)的委托。
處理完回發(fā)事件之后,頁(yè)面就可以顯示了。這個(gè)階段的標(biāo)志是 PreRender 事件??丶梢岳眠@段時(shí)間來(lái)執(zhí)行那些需要在保存視圖狀態(tài)和顯示輸出的前一刻執(zhí)行的更新操作。下一個(gè)狀態(tài)是 SaveViewState,在此狀態(tài)中,所有控件和頁(yè)面本身都將更新自己 ViewState 集合的內(nèi)容。然后,將得到序列化、散列、Base64 編碼的視圖狀態(tài),而且此視圖狀態(tài)與隱藏字段 __VIEWSTATE 相關(guān)聯(lián)。
通過(guò)覆蓋 Render 方法可以改變各個(gè)控件的顯示機(jī)制。此方法接受 HTML 書寫器對(duì)象,并使用此對(duì)象來(lái)積累所有要為控件生成的 HTML 文本。Page 類的 Render 方法的默認(rèn)實(shí)現(xiàn)包括對(duì)所有成員控件的遞歸調(diào)用。對(duì)于每個(gè)控件,頁(yè)面都將調(diào)用 Render 方法,并緩存 HTML 輸出。
頁(yè)面生命中的最后一個(gè)標(biāo)志是 Unload 事件,在頁(yè)面對(duì)象消除之前發(fā)生。在此事件中,您應(yīng)該釋放所有可能占用的關(guān)鍵資源(例如文件、圖形對(duì)象、數(shù)據(jù)庫(kù)連接等)。
在此事件之后,也就是最后,瀏覽器接收 HTTP 響應(yīng)數(shù)據(jù)包并顯示頁(yè)面。
小結(jié)ASP.NET 頁(yè)面對(duì)象模型因其事件機(jī)制而顯得格外新穎獨(dú)特。Web 頁(yè)面由控件組成,這些控件既可以產(chǎn)生豐富的基于 HTML 的用戶界面,又可以通過(guò)事件與用戶交互。以前,在 Web 應(yīng)用程序的上下文中設(shè)置事件模型是件有挑戰(zhàn)性的工作??晌覀凅@奇的看到,客戶端生成的事件可以由服務(wù)器端的代碼來(lái)解決,而且只進(jìn)行一些相應(yīng)的修改后,此過(guò)程仍可以輸出相同的 HTML 頁(yè)面。
掌握這個(gè)模型對(duì)于了解頁(yè)面生命周期的各個(gè)階段,以及頁(yè)面對(duì)象如何被 HTTP 運(yùn)行時(shí)實(shí)例化并使用是非常重要的。
如對(duì)本文有疑問(wèn),請(qǐng)?zhí)峤坏浇涣髡搲?,廣大熱心網(wǎng)友會(huì)為你解答!! 點(diǎn)擊進(jìn)入論壇