天天看點

Asp.net頁面生命周期

前言                                    

       《亮劍.net 深入體驗與實戰精要》

正文                                    

1.頁面執行個體化之前:asp.net工作程序會确定是否需要分析和編譯頁面進而開始生命周期,或是否從緩存中讀取已生成好的html頁面而不開始生命周期;

2.頁面執行個體化:這個階段會檢查該請求是否為回傳,并且設定ispostback和uiculture屬性等。(2012/9/27補充:此時httpcontext.current.session對象未執行個體化,是以無法引用)

3.頁面預初始化(onpreinit):此階段a.将初始化在aspx檔案聲明的伺服器控件和頁面,當然也可以在這裡生成動态伺服器控件,并生成

頁面的控件樹;b.動态設定theme屬性。注意此時隻是初始化了伺服器控件和頁面的架構和聲明時設定的屬性,而viewstate等還沒有恢複,也不存

在回傳值(但可以通過request.form來擷取有效控件的回傳值,隻是還沒複制到控件執行個體中。因為request對象不是在httphandler

4.頁面初始化(oninit):讀取頁面和控件的值,生成動态伺服器控件。(2012/5/25補充:初始化後:begin tracking view state,此後的viewstate才會儲存起來 )

(2012/9/27補充:此時httpcontext.current.session已經執行個體化了)

5.頁面初始化完成(oninitcomplete):處理要求先完成所有初始化工作。(暫時不清楚哪些功能點要用到它)

6.加載頁面狀态(loadpagestatefrompersistencemedium):該事件隻在ispostback為true時觸發

(是以ispostback等屬性要在執行個體化時就設定好了!)。注意該事件加載的是頁面狀态而不單單是viewstate,頁面狀态

(pagestate)包含viewstate和controlstate。其中viewstate又有頁面的viewstate和控件的

viewstate,而這裡加載的viewstate中包含了這兩種。該事件是管加載,不管恢複,是以執行該方法後控件和頁面依然沒有回傳值和

viewstate值。(這裡的controlstate具體用法有待研究:2012/5

/25補充:controlstate是一種特殊的viewstate,即使頁面或網站禁用了viewstate,controlstate依然起作用。

在自定義控件時,涉及到的方法有loadcontrolstate、savecontrolstate,要使用controlstate必須向頁面注冊

controlstate,注冊方法為:page.registerrequirescontrolstate(this),這樣就可以用

controlstate儲存控件的資訊了!)

7.恢複頁面viewstate(loadviewstate):如果上一步中加載的viewstate中含有頁面viewstate那麼該事件将

會被觸發,否則跳過。什麼是頁面viewstate呢?其實就是直接以viewstate[key]=value形式設定的viewstate。恢複後調

用viewstate[key]就得到上次請求設定的值了!

8.恢複控件viewstate(控件的loadviewstate):每個伺服器控件的祖父均為control類,伺服器控件就是通過繼承

control類的loadviewstate方法來恢複viewstate的。同樣如果在第6步中加載的viewstate含有該控件的

viewstate,那麼就執行該方法;

9.擷取控件的回傳值并設定控件到相應的屬性上(控件的loadpostdata):存在回傳值的伺服器控件均繼承了

ipostbackdatahandler接口,并實作了loadpostdata方法和raisepostdatachangedevent事

件。 protected override bool loadpostdata(string postdatakey,

system.collections.specialized.namevaluecollection

postcollection),其中參數postdatakey為伺服器控件的伺服器端id,用于辨別取哪個控件的回傳值;參數

postcollection是所有回傳值的集合,就是request.form裡面有的這裡都有。該方法會在postcollection中擷取

postdatakey對應的回傳值,然後跟第8步中恢複的viewstate值作對比(如果沒執行第8步,則與控件的預設值作對比),如果不同則傳回

true,否則傳回false。2012/09/04新增:對于傳回true的對象,将會儲存其raisepostdatachangedevent事件到一個數組中,供後期使用。

好了,現在在aspx檔案上聲明的控件的狀态和回傳值都已經恢複和設定到控件執行個體中了。讓我們繼續探讨吧!

10.頁面加載(onload==page_load):我想大家都十分熟悉這個事件了,這裡我們可以随心所欲地操作aspx檔案上聲明的控件了,

但除了在該事件中執行個體化的伺服器控件。在該事件中可以執行個體化伺服器控件并将其加入到頁面的控件樹中,就是form1.controls.add(伺服器控

件執行個體)。如果ispostback為true,因為此時執行個體化的控件沒有參與步驟8到9,是以當該控件加入到頁面控件樹時就會進入步驟8,執行完繼續執

行page_load的其餘代碼,但這些控件還沒獲得回傳值;

  注意點1:以下情況執行個體化控件的話,将無法直接通過“控件變量.屬性”的形式擷取執行個體化控件的回傳值,要通過this.form1.findcontrol等形式擷取。

可以改成這樣來直接擷取

注意點2:對于dropdownlist控件

   當在aspx聲明dropdownlist控件,在page_load事件中添加option項可以這樣寫

回傳時會發現這兩個option項均儲存在控件viewstate中。

       當在page_load事件中執行個體化dropdownlist并加入頁面控件樹時(如下面法)

回傳時發現這兩個option項均沒有儲存在控件viewstate中。是以導緻無法恢複option項。是以必須每次page_load都完全重新生成一次,如下:

至于ms為什麼這樣設計還有待研究,求解答!

2012/5/25更新:

 對于上面的問題在上陣子學習自定義控件時找到了答案,現在補充一下吧!這裡涉及到

容易犯的誤區——隻要開啟的viewstate,一切伺服器控件的資料都将儲存在viewstate中。其實不是這樣,隻有該控件執行了

trackviewstate後,在該控件上設定/修改的資料才會儲存到viewstate中。那什麼是trackviewstate呢?那麼我們要認識

一個接口istatemanager,asp.net規定每個需要使用viewstate的類必須繼承istatemanager接口,而

trackviewstate就是這個接口裡面的方法,而該接口中還有一個隻讀屬性

istrackingviewstate,trackviewstate就是用來修改istrackingviewstate傳回的值的,隻有istrackingviewstate傳回true,那麼該控件的值才會在saveviewstate中儲存到viewstate中(當然我們可以重寫的時候讓istrackingviewstate永遠傳回false,那麼控件的資料就無法儲存到viewstate了)。

而trackviewstate的是在控件初始化的末期執行的,而上面的情況ddl先經曆執行個體化,然後就添加清單項,在添加到頁面控件樹裡面,當加入頁面

控件樹時ddl會馬上追趕頁面的生命周期到達“加載”這個階段,當然ddl的trackviewstate在這時已經執行了,所有後面對ddl的修改将保

存到viewstate中,但應添加清單項的操作時再trackviewstate執行前的,是以清單項就不會儲存到viewstate中。如果先把

ddl添加到頁面控件樹再添加清單項,那麼清單項将會儲存到viewstate當中去。

  再補充——生命周期追趕:在背景代碼中動态生成控件時,控件會處于其生命周期中的“執行個體化”階段,當加入到頁面控件樹時就會同步到頁面目前的生命周期階段,而兩個階段之間的各個階段控件都會經曆

11.擷取在page_load中執行個體化的控件的回傳值并設定控件到相應的屬性上(控件的loadpostdata):過程跟步驟9一樣,隻是給在page_load中執行個體化的控件一個得到回傳值的機會,要好好珍惜哦!

12.控件回傳值變化事件(raisepostdatachangedevent):2012/09/04修改:這裡會周遊第9步和第11步中的儲存raisepostdatachangedevent事件的數組,并逐一執行事件的處理函數對于第9步和第11步傳回true的控件就會觸發該事件,注意這裡是一堆控件一起觸發事件。(具體用途有待研究)

13.raisepostbackevent:但點選button和imagebutton時會觸發;(具體作用有待研究)

2012/5/25補充:

  raisepostbackevent不單單是點選button和

imagebutton時會觸發,其實隻要回傳操作都會觸發。asp.net規定能實作通過點選、值變更等操作而觸發回傳操作的控件必須繼承

ipostbackeventhandler接口,而raisepostbackevent就是該接口的方法。控件的所有上述回傳操作都會觸發raisepostbackevent方法,然後根據實際情況配置設定給不同的函數去處理。對于頁面,頁面上所有控件的上述回傳操作均會觸發raisepostbackevent方法,然後根據參數的不同由不同的函數去處理,而我們平常習慣在asp:button上onclick寫事件處理函數,其實該事件處理函數就是通過raisepostbackevent根據不同的參數來指定該函數來處理回傳的。

14.頁面驗證(validate):在ispostback為true并且頁面有驗證web伺服器控件時觸發。

15.回發事件處理:如button的click事件處理程式;注意——若在這裡添加伺服器控件,那麼将不會觸發loadviewstate和loadpostback。

16.頁面加載完成(onloadcomplete):此時頁面加載完成了,伺服器控件均完整并可用;

17.頁面預呈現(onprerender):這裡是設定控件屬性并該設定能儲存到viewstate的最後地方,當然也可以在第18步中設定;在

該方法執行前會先執行頁面和控件的ensurechildcontrol方法和執行設定了datasourceid屬性的控件的databind事件。

18.頁面預呈現完成(onprerendercomplete);

17.儲存頁面狀态(savepagestatetopersistencemedium):這裡預設會将頁面viewstate、控件

viewstate和控件controlstate等按base64編碼序列化,儲存到一個隐藏控件中。經過該事件後,再設定控件的屬性(如

textbox的text、cssclass屬性等,viewstate會儲存控件的所有屬性),結果能呈現到用戶端,但回傳時控件的viewstate依然為舊值;頁面viewstate道理一樣。如果設定了viewstate分塊儲存的話,會将viewstate分塊儲存在多個隐藏控件中。如下

如果隐藏域中的資料量過大,某些代理和防火牆将阻止對包含這些資料的頁的通路。由于最大數量會随所采用的防火牆和代理的不同而不同,較大的隐藏域可能會出現偶發性問題。如果您需要存儲大量的資料項,可以打開視圖狀态分塊,這樣會自動将資料分割到多個隐藏域。 asp.net架構提供了maxpagestatefieldlength屬性,用來擷取或設定頁狀态字段的最大長度。其屬性值表示頁面狀态字段的最大長度,以位元組為機關。 微軟官方網站以及很多文章介紹說,通過設定page.maxpagestatefieldlength屬性可以指定塊的最大位元組數,且msdn明确 說明此屬性是公有的,筆者在vs 2005和vs 2008下測試結果是page下面根本沒有這個屬性,是以采用在配置檔案web.config中實作,如下: <system.web>      <pages maxpagestatefieldlength="100" />       當maxpagestatefieldlength屬性設定為正數時,發送到用戶端浏覽器的視圖狀态将分為多個隐藏字段,并且每個字段的值都小于在 maxpagestatefieldlength屬性中指定的大小;而如果maxpagestatefieldlength屬性設定為負數(預設值),則 表示不應将視圖狀态字段分成多個塊區。另外,如果将maxpagestatefieldlength設定非常小,會導緻性能降低。

18.呈現(render):此時對頁面請求的處理算是告一段落,這裡會将整個頁面轉換成html頁面并儲存到一個htmltextwriter對

象中,該對象會傳遞到response.outputstream中傳回給用戶端;(可以在這事件中截取轉換後的html進行加工,然後将結果html字

19.釋放資源(dispose):執行銷毀控件前的所有最終清理操作。在此階段必須釋放對昂貴資源的引用,如記憶體的退出、資料庫的連接配接等。

20.解除安裝(onunload):頁面生命周期正式結束。

結束語                                   

  asp.net頁面生命周期中還有很多地方值得深入學習,這裡隻是作個小結和介紹,以後慢慢完善吧!!