通常服務器控件的生命週期包含11個階段:數據庫
/// <summary> /// 1. 初始化 /// </summary> /// <remarks> /// 1. 主要完成控件的初始化,頁面經過ProcessRequest方法來遞歸它的子控件並依次調用對應的OnInit方法; /// 2. 經過調用TrackViewState方法打開控件的視圖狀態跟蹤功能,以便存儲在ViewState中的值在頁面回發時能正確的恢復到控件上。 /// </remarks> protected override void OnInit(EventArgs e) { Write("1. OnInit"); base.OnInit(e); this.Page.RegisterRequiresPostBack(this); } /// <summary> /// 2. 加載視圖 /// </summary> /// <remarks> /// 本階段僅在頁面回發時執行,事實上頁面在第一次執行時尚未得到存在到視圖狀態中的數據,主要完成加載視圖狀態到控件的過程, /// 前提是該控件啓用視圖狀態,在客戶端請求過程當中,視圖狀態將存儲到一個隱藏域中並回傳到服務器,以便頁面再次回發時以爲是否執行回發事件。 /// </remarks> protected override void LoadViewState(object savedState) { Write("2. LoadViewState"); base.LoadViewState(savedState); } /// <summary> /// 3. 數據回傳處理 /// </summary> /// <param name="postDataKey">裝載了客戶端提交的頁面控件ID</param> /// <param name="postCollection">裝載了客戶端提交的數據</param> /// <remarks> /// 本階段僅在頁面回發時執行,主要完成控件數據回傳功能,根據客戶端提交數據的新值和舊值比較是否執行RaisePostDataChangedEvent方法, /// 客戶端修改窗體數據提交後,將以「&」形式隔開並將數據與ID一一對應起來,根據控件ID來檢測是否實現IPostBackDataHandler接口,若是實現 /// 纔會調用LoadPostData方法,給其刷新其值的機會。 /// </remarks> public bool LoadPostData(string postDataKey, NameValueCollection postCollection) { Write("3. LoadPostData"); return true; } /// <summary> /// 4. 加載事件 /// </summary> /// <remarks> /// 先執行頁面的Page_Load事件再遞歸執行控件的OnLoad事件,主要完成建立控件及初始化,根據視圖狀態還原控件客戶端數據, /// 常常經過IsPostBack來判斷控件是第一次請求頁面仍是在回發事件中進行相應的代碼邏輯處理。 /// </remarks> protected override void OnLoad(EventArgs e) { Write("4. OnLoad"); base.OnLoad(e); } /// <summary> /// 5. 回傳事件通知 /// </summary> /// <remarks> /// 本階段僅在頁面回發時執行,此方法也是接口IPostBackDataHandler的方法,只有當LoadPostData爲true時,RaisePostDataChangedEvent纔會被調用。 /// </remarks> public void RaisePostDataChangedEvent() { Write("5. RaisePostDataChangedEvent"); } /// <summary> /// 6. 處理回發事件 /// </summary> /// <remarks> /// 本階段僅在頁面回發時執行,主要引起回發的客戶端事件,成功捕獲回發的客戶端事件並在服務器端進行處理。前提是必須實現IPostBackEventHandler接口, /// 根據參數eventArgument來判斷是哪一個控件觸發的回發事件,進而完成相應的事件處理。 /// </remarks> public void RaisePostBackEvent(string eventArgument) { Write("6. RaisePostBackEvent"); } /// <summary> /// 7. 預呈現 /// </summary> /// <remarks> /// 主要完成控件呈現以前的一些操做,註冊控件相應的資源,如:Javascript腳本和隱藏域控件等。 /// </remarks> protected override void OnPreRender(EventArgs e) { Write("7. OnPreRender"); base.OnPreRender(e); } /// <summary> /// 8. 保存視圖狀態 /// </summary> /// <remarks> /// 與LoadViewState正好相反,主要完成存儲頁面視圖狀態信息,同時將在頁面第一次請求時就會執行,而LoadViewState僅在頁面回發時才執行。 /// </remarks> protected override object SaveViewState() { Write("8. SaveViewState"); base.SaveViewState(); return new Pair(); } /// <summary> /// 9. 呈現 /// </summary> /// <remarks> /// 將控件和字符文本輸出到服務器控件輸出流中,以HTML的形式呈如今客戶端。對於控件而言,可調用RenderControl方法輸出流。 /// </remarks> protected override void Render(HtmlTextWriter writer) { writer.Write("<input type='button' name='{0}' value='Click Me!' style='position:absolute;left:20px;top:280px' onclick=\"{1}\"/>", this.UniqueID, Page.ClientScript.GetPostBackEventReference(this, "")); Write("9. Render"); base.Render(writer); } /// <summary> /// 10. 卸載 /// </summary> /// <remarks> /// 主要完成頁面及控件資源的清理,不能等同於當離開一個頁面或關閉瀏覽器時觸發,由於依靠頁面視圖狀態使得兩次請求看起來是連續的,而兩次請求間是無狀態的。 /// </remarks> protected override void OnUnload(EventArgs e) { Write("10. OnUnload"); base.OnUnload(e); } /// <summary> /// 11. 釋放 /// </summary> /// <remarks> /// 釋放一些資源,如數據庫鏈接或IO文件流。 /// </remarks> public override void Dispose() { Write("11. Dispose"); base.Dispose(); } private void Write(string text) { HttpContext.Current.Response.Write(text + "<br/>"); }
注:像Page這樣的容器型服務器控件具備更細化的生命週期階段。如對於每個控件而言,都只有一個Init事件,而Page控件又細化爲PreInit、Init、InitComplete三個階段,一般PreInit階段用於設置模板頁和主題的屬性,一旦到了Init階段將不能再改變,Init階段將依次調用各子控件的Init事件來初始化並設置命名容器,而InitComplete階段將啓用控件具備視圖狀態跟蹤能力。設計模式
設計模式是爲開發者而設計,使開發者能及時看到控件的展示效果以便快捷設置控件的屬性和行爲,但在設計模式下將不執行某些控件生命週期的事件(如OnPreRender,Load,CreateChildControls等),也不存在僅在運行模式下的上下文環境變量,但Init, Construct, Render, RenderContents, Dispose等事件都會在設計模式下執行。如OnPreRender事件要引入一些資源文件(Javascript/CSS/Pictures), 在IDE設計器狀態下時文件路徑是不可取的,它必須根據當前運行模式下的虛擬服務器路徑來獲取。再好比在Page控件的PageLoad事件中引用了服務器的上下文環境,也將報"獲取不到信息"的異常錯誤。瀏覽器
每個服務器控件都由ID,UniqueID和ClientID來惟一標識,其中ID是咱們命名的ID,UniqueID是服務器端的ID,ClientID是客戶端的ID,一般狀況下控件沒有做爲子控件或在MasterPage下時,這三者是徹底一致的,若是控件繼承了INamingContainer接口,UniqueID和ClientID將以父控件的this.UniqueID加上ID並分別以不一樣的分隔符($和_)來標識。服務器