<form id="hz9zz"></form>
  • <form id="hz9zz"></form>

      <nobr id="hz9zz"></nobr>

      <form id="hz9zz"></form>

    1. 明輝手游網中心:是一個免費提供流行視頻軟件教程、在線學習分享的學習平臺!

      ASP.Net Web Page深入探討二

      [摘要]五、頁面生存周期現在回到第三個標題中講到的內容,我們講到了HttpApplication的實例接收請求,并創建頁面類的實例,實際上這個實例也就是動態編譯的ASPX的類的一個實例,上一個標題中我們了解到ASPX實際上是代碼綁定中類的子類,所以它繼承了所有的protected方法,F在我們來看看VS....

      五、頁面生存周期

      現在回到第三個標題中講到的內容,我們講到了HttpApplication的實例接收請求,并創建頁面類的實例,實際上這個實例也就是動態編譯的ASPX的類的一個實例,上一個標題中我們了解到ASPX實際上是代碼綁定中類的子類,所以它繼承了所有的protected方法。

      現在我們來看看VS.Net自動生成的CodeBehind類的代碼,以此來開始我們對頁面生命周期的探討:

      #region Web Form Designer generated code

      override protected void OnInit(EventArgs e)

      {

      //

      // CODEGEN:該調用是 ASP.NET Web 窗體設計器所必需的。

      //

      InitializeComponent();

      base.OnInit(e);

      }

      /// <summary>

      /// 設計器支持所需的方法 - 不要使用代碼編輯器修改

      /// 此方法的內容。

      /// </summary>

      private void InitializeComponent()

      {

      this.DataGrid1.ItemDataBound += new System.Web.UI.WebControls.DataGridItemEventHandler(this.DataGrid1_ItemDataBound);

      this.Load += new System.EventHandler(this.Page_Load);

      }

      #endregion

      這個就是使用VS.Net產生的Page的代碼,我們來看,這里面有兩個方法,一個是OnInit,一個是InitializeComponent,后者被前者調用,實際上這就是頁面初始化的開始,在InitializeComponent中我們看到了控件的事件聲明和Page的Load聲明。

      下面是從MSDN中摘錄的一段描述和一個頁面生命周期方法和事件觸發的順序表:

      “每次請求 ASP.NET 頁時,服務器就會加載一個 ASP.NET 頁,并在請求完成時卸載該頁。頁及其包含的服務器控件負責執行請求并將 HTML 呈現給客戶端。雖然客戶端和服務器之間的通訊是無狀態的和斷續的,但是必須使客戶感覺到這是一個連續執行的過程。”

      “這種連續性假象是由 ASP.NET 頁框架、頁及其控件實現的;匕l后,控件的行為必須看起來是從上次 Web 請求結束的地方開始的。雖然 ASP.NET 頁框架可使執行狀態管理相對容易一些,但是為了獲得連續性效果,控件開發人員必須知道控件的執行順序。控件開發人員需要了解:在控件生命周期的各個階段,控件可使用哪些信息、保持哪些數據、控件呈現時處于哪種狀態。例如,在填充頁上的控件樹之前控件不能調用其父級!

      “下表提供了控件生命周期中各階段的高級概述。有關詳細信息,請點擊表中的鏈接!

      階段
      控件需要執行的操作
      要重寫的方法或事件

      初始化
      初始化在傳入 Web 請求生命周期內所需的設置。請參閱處理繼承的事件。
      Init 事件(OnInit 方法)

      加載視圖狀態
      在此階段結束時,就會自動填充控件的 ViewState 屬性,詳見維護控件中的狀態中的介紹?丶梢灾貙 LoadViewState 方法的默認實現,以自定義狀態還原。
      LoadViewState 方法

      處理回發數據
      處理傳入窗體數據,并相應地更新屬性。請參閱處理回發數據。

      注意 只有處理回發數據的控件參與此階段。
      LoadPostData 方法

      (如果已實現 IPostBackDataHandler)

      加載
      執行所有請求共有的操作,如設置數據庫查詢。此時,樹中的服務器控件已創建并初始化、狀態已還原并且窗體控件反映了客戶端的數據。請參閱處理繼承的事件。
      Load 事件

      (OnLoad 方法)

      發送回發更改通知
      引發更改事件以響應當前和以前回發之間的狀態更改。請參閱處理回發數據。

      注意 只有引發回發更改事件的控件參與此階段。
      RaisePostDataChangedEvent 方法

      (如果已實現 IPostBackDataHandler)

      處理回發事件
      處理引起回發的客戶端事件,并在服務器上引發相應的事件。請參閱捕獲回發事件。

      注意 只有處理回發事件的控件參與此階段。
      RaisePostBackEvent 方法

      (如果已實現 IPostBackEventHandler)

      預呈現
      在呈現輸出之前執行任何更新。可以保存在預呈現階段對控件狀態所做的更改,而在呈現階段所對的更改則會丟失。請參閱處理繼承的事件。
      PreRender 事件

      (OnPreRender 方法)

      保存狀態
      在此階段后,自動將控件的 ViewState 屬性保持到字符串對象中。此字符串對象被發送到客戶端并作為隱藏變量發送回來。為了提高效率,控件可以重寫 SaveViewState 方法以修改 ViewState 屬性。請參閱維護控件中的狀態。
      SaveViewState 方法

      呈現
      生成呈現給客戶端的輸出。請參閱呈現 ASP.NET 服務器控件。
      Render 方法

      處置
      執行銷毀控件前的所有最終清理操作。在此階段必須釋放對昂貴資源的引用,如數據庫鏈接。請參閱 ASP.NET 服務器控件中的方法。
      Dispose 方法

      卸載
      執行銷毀控件前的所有最終清理操作?丶髡咄ǔT Dispose 中執行清除,而不處理此事件。
      UnLoad 事件(On UnLoad 方法)

      從這個表里面我們可以清楚的看到一個Page從裝載到卸載之間調用的方法和觸發的時間,接下來我們就深入的對其進行一些分析。

      看了上面的表,細心的朋友可能要問了,既然OnInit是頁面生命周期的開始,而我們在上一講中談到控件在子類中被創建,那么在這里實際上在InitializeComponent方法中我們已經可以使用父類中聲名的字段了,那么就意味著子類的初始化更在這之前?

      在第三個標題中我們講到了頁面類的ProcessRequest才是真正意義上的頁面聲明周期的開始,這個方法是由HttpApplication調用的(其中調用的方式比較復雜,有機會單獨撰文來講解),一個Page對請求的處理就是從這個方法開始,通過反編譯.Net類庫來查看源代碼,我們發現在System.Web.WebControls.Page的基類:System.Web.WebControls.TemplateControl(它是頁面和用戶控件的基類)中定義了一個“FrameworkInitialize”虛擬方法,然后在Page的ProcessRequest中最先調用了這個方法,在生成器生成的ASPX的源代碼中我們發現了這個方法的蹤影,所有的控件都在這個方法中被初始化,頁面的控件樹就在這個時候產生。

      接下來的事情就簡單了,我們來逐步分析頁面生命周期的每一項:

      1、 初始化

      初始化對應Page的Init事件和OnInit方法。

      如果要重寫,MSDN推薦的方式是重載OnInti方法,而不是增加一個Init事件的代理,這兩者是有差別的,前者可以控制調用父類OnInit方法的順序,而后者只能在父類的OnInit后執行(實際上是在OnInit里面被調用的)。

      2、 加載視圖狀態

      這是個比較重要的方法,我們知道,對于每次請求,實際上是由不同的頁面類實例來處理的,為了保證兩次請求間的狀態,ASP.Net使用了ViewState,關于ViewState的描述,請參考本人的另一篇文章:

      http://expert.csdn.net/Expert/topic/1558/1558798.xml?temp=.2561609

      LoadViewState方法就是從ViewState中獲取上一次的狀態,并依照頁面的控件樹的結構,用遞歸來遍歷整個樹,將對應的狀態恢復到每一個控件上。

      3、 處理回發數據

      這個方法是用來檢查客戶端發回的控件數據的狀態是否發生了改變。方法的原型:

      public virtual bool LoadPostData(string postDataKey, NameValueCollection postCollection)

      postDataKey是標識控件的關鍵字(也就是postCollection中的Key),postCollection是包含回發數據的集合,我們可以重寫這個方法,然后檢查回發的數據是否發生了變化,如果是則返回一個True,“如果控件狀態因回發而更改,則 LoadPostData 返回 true;否則返回 false。頁框架跟蹤所有返回 true 的控件并在這些控件上調用 RaisePostDataChangedEvent。”(摘自MSDN)

      這個方法是System.Web.WebControls.Control中定義的,也是所有需要處理事件的自定義控件需要處理的方法,對于我們今天討論的Page來說,可以不用管它。

      4、 加載

      加載對應Load事件和OnLoad方法,對于這個事件,相信大多數朋友都會比較熟悉,用VS.Net生成的頁面中的Page_Load方法就是響應Load事件的方法,對于每一次請求,Load事件都會觸發,Page_Load方法也就會執行,相信這也是大多數人了解ASP.Net的第一步。

      Page_Load方法響應了Load事件,這個事件是在System.Web.WebControl.Control類中定義的(這個類是Page和所有服務器控件的祖宗),并且在OnLoad方法中被觸發。

      很多人可能碰到過這樣的事情,寫了一個PageBase類,然后在Page_Load中來驗證用戶信息,結果發現不管驗證是否成功,子類頁面的Page_Load總是會先執行,這個時候很可能留下一些安全性的隱患,用戶可能在沒有得到驗證的情況下就執行了子類中的Page_Load方法。

      出現這個問題的原因很簡單,因為Page_Load方法是在OnInit中被添加到Load事件中的,而子類的OnInit方法中是先添加了Load事件,然后再調用base.OnInit,這樣就造成了子類的Page_Load被先添加,那么先執行了。

      要解決這個問題也很簡單,有兩種方法:

      1) 在PageBase中重載OnLoad方法,然后在OnLoad中驗證用戶,然后調用base.OnLoad,因為Load事件是在OnLoad中觸發,這樣我們就可以保證在觸發Load事件之前驗證用戶。

      2) 在子類的OnInit方法中先調用base.OnInit,這樣來保證父類先執行Page_Load

      5、 發送回發更改通知

      這個方法對應第3步的處理回發數據,如果處理回發數據返回True,頁面框架就會調用此方法來觸發數據更改的事件,所以自定義控件的回發數據更改事件需要在此方法中觸發。

      同樣這個方法對于Page來說,沒有太大的用處,當然你也可以在Page的基礎上自己定義數據更改的事件,這當然也是可以的。

      6、 處理回發事件

      這個方法是大多數服務器控件事件引發的地方,當請求中包含控件事件觸發的信息時(服務器控件的事件是另一個論題,我會在不久將來另外撰文討論),頁面控件會調用相應控件的RaisePostBackEvent方法來引發服務器端的事件。

      這里又引出一個常見的問題:

      經常有網友問,為什么修改提交后的數據并沒有更改

      多數的情況都是他們沒有理解服務器事件的觸發流程,我們可以看出,觸發服務器事件是在Page的Load之后,也就是說頁面會先執行Page_Load,然后才會執行按鈕(這里以按鈕為例)的點擊事件,很多朋友都是在Page_Load中綁定數據,然后在按鈕事件中處理更改,這樣做有一個毛病,Page_Load永遠都是在按鈕事件之前執行,那么意味著數據還沒來得及更改,Page_Load中的數據綁定的代碼就先執行了,原有的數據又賦給了控件,那么執行按鈕事件的時候,實際上獲得的是原有的數據,那么更新當然就沒有效果了。

      更改這個問題也非常簡單,比較合理的做法是把數據綁定的代碼寫成一個方法,我們假設為BindData:

      private void BindData()

      {

      //綁定數據

      }

      然后修改PageLoad:

      private void Page_Load( object sender,EventArgs e )

      {

      if( !IsPostBack )

      {

      BindData(); //在頁面第一次訪問的時候綁定數據

      }

      }

      最后在按鈕事件中:

      private Button1_Click( object sender,EventArgs e )

      {

      //更新數據

      BindData();//重新綁定數據

      }

      7、 預呈現

      最終請求的處理都會轉變為發回服務器的響應,預呈現這個階段就是執行在最終呈現之前所作的狀態的更改,因為在呈現一個控件之前,我們必須根據它的屬性來產生Html,比如Style屬性,這是最典型的例子,在預呈現之前,我們可以更改一個控件的Style,當執行預呈現的時候,我們就可以把Style保存下來,作為呈現階段顯示Html的樣式信息。

      8、 保存狀態

      這個階段是針對加載狀態的,我們多次提到,請求之間是不同的實例在處理,所以我們需要把本次的頁面和控件的狀態保存起來,這個階段就是把狀態寫入ViewState的階段。

      9、 呈現

      到這里,實際上頁面對請求的處理基本就告一段落了,在Render方法中,會遞歸整個頁面的控件樹,依次調用Render方法,把對應的Html代碼寫入最終響應的流中。

      10、處置

      實際上就是Dispose方法,在這個階段會釋放占用的資源,例如數據庫連接。

      11、卸載

      最后,頁面會執行OnUnLoad方法觸發UnLoad事件,處理在頁面對象被銷毀之前的最后處理,實際上ASP.Net提供這個事件只是設計上的考慮,通常資源的釋放都會在Dispose方法中完成,所以這個方法也變成雞肋了。

      我們簡單的介紹了頁面的生存周期,對于服務器端事件的處理做了不太深入的講解,今天主要是想大家了解頁面執行的周期,對于服務器控件的事件和生存期我會在后續在寫一些文章來探討。

      這些內容是我在學習ASP.Net的時候對Page研究的一些心得,具體的細節沒有很詳細的探討,更多的內容請大家參考MSDN,但是我舉了一些初學者常犯的錯誤和出現錯誤的原因,希望可以給大家帶來啟發





      日韩精品一区二区三区高清