最近一段時間在使用Microsoft SharePoint2007,進行開發一個Document 管理系統,裡面有使用SharePoint Designer設計Page佈局,需要使用自定義開發的Webpart 控件,由於本來就沒有進行搞控件開發了,再加上有句名言“知識就是力量”,那沒得知識就沒得力量了,所以沒得辦法,就利用Baidu + Google,googling了半下午。結果再次驗證了Google< /SPAN>的搜索實力確實比Baidu強悍啊,在此為Google最近的決定感到失望,牢騷就不多發了,還是進正題。以下是自己的一些新的總結和找到的一些學習資源。希望可以share下.
首先了解到,Page類繼承自模板控件類TemplateControl 和Http 處理程序接口IHttpHandler,
1.TemplateControl類是個abstract類,繼承自Control類,ID命名容器控件INamingContainer,
和設備篩選器接口IFilterResolutionService< SPAN style="LINE-HEIGHT: 18px; FONT-FAMILY: 宋體; FONT-SIZE: 9pt">。
2. IHttpHandler接口是為了使用Http處理程序同步處理Http Web請求而實現的協定。有一個只Set的IsReusable屬性和一個方法ProcessRequest(HttpContext context)方法,用來處理Http Web請求。
既然Page間接的繼承自Control類,那下面就先從介紹Control類說起。
Control類的生命週期有七個大的事件階段,分別為Init事件,< SPAN style="LINE-HEIGHT: 18px">Load事件,DataBinding事件,PreRender 事件,Render方法,Unload事件,Disposed事件。下面簡單介紹下每個階段;
1.Init階段,引發Init事件,執行OnInit(EventArgs e)虛方法。詳細請參見處理繼承的事件。
2.然後執行TrackViewState(object savedState)虛方法,會根據頁面IsPostBack屬性,來確定是否LoadViewState< /SPAN>請參見處理回發數據和維護控件中的狀態(objcet savedState)來加載視圖狀態,以及如果控件繼承自接口IPostBackDataHandler,還會實現< STRONG>LoadPostData(string postDataKey,System.Collections.Specialized.NameValueCollection postCollection), 來處理回發數據。詳細
3.Load階段,引發Load事件,執行OnLoad(EventArgs e)虛方法。此時,Load事件樹中的服務器控件已創建並初始化、狀態已還原並且窗體控件反映了客戶端的數據。詳細參見 處理繼承的事件。
4.之後根據頁面IsPostBack屬性,如果實現了接口IPostBackDataHandler, 則調用RaisePostDataChangedEvent() 方法,來處理引發更改事件以響應當前和以前回發之間的狀態更改。如果實現如果已實現 IPostBackEventHandler接口,則引發 RaisePostBackEvent(string eventArgument)方法的實現來處理,來處理引起回發的客戶端事件,並在服務器上引發相應的事件。 詳細參見< SPAN style="LINE-HEIGHT: 18px; FONT-FAMILY: 宋體">處理回發數據和 捕獲回發事件。
5.PreRender階段,引發PreRender 事件,執行OnPreRender< /SPAN>方法,在呈現輸出之前執行任何更新。可以保存在預呈現階段對控件狀態所做的更改,而在呈現階段所對的更改則會丟失。此階段是對控件做任何更新的最後機會。詳細請參見處理繼承的事件。 (EventArgs e)
6.SaveViewState階段,在此階段後,自動將控件的 View State 屬性保持到字符串對像中。此字符串對像被發送到客戶端並作為隱藏變量發送回來。為了提高效率,控件可以重寫 SaveViewState() 方法以修改 View State屬性。 詳細請參見 維護控件中的狀態。
7.呈現Render階段,此階段沒有不適事件,執行Render()方法,來生成呈現給客戶端的輸出。 詳細參見< SPAN style="LINE-HEIGHT: 18px; COLOR: #000000">呈現 ASP.NET 服務器控件< /SPAN>。 方法,以便動態設置區域性信息。注意,在使用 Page_事件語法創建事件處理程序時,將隱式調用基實現,因此無需在方法中調用它。例如,無論是否創建 Page_Load 方法,始終都會調用頁基類的 OnLoad 方法。但是,如果使用 override 關鍵字(在 Visual Basic 中為 Overrides< /SPAN>)重寫頁的 OnLoad 方法,則必須顯式調用基方法。例如,如果在頁中重寫 OnLoad 方法,則必須調用 base.Load(在 Visual Basic 中為 MyBase.Load)以運行基實現
8.Dispose階段< /SPAN>,執行Disponse( )方法,執行銷毀控件前的所有最終清理操作。在此階段必須釋放對昂貴資源的引用,如數據庫鏈接。 詳細參見< SPAN style="LINE-HEIGHT: 18px; COLOR: #000000">ASP.NET 服務器控件中的方法。 方法(並調用 base.OnEventName )。
9.卸載Unload< /STRONG>階段,引發Unload事件,執行OnUnload()方法,來執行銷毀控件前的所有最終清理操作。控件作者通常在 Dispose 中執行清除,而不處理此事件。
以下2點要有所注意:
1.要重寫 Event Name 事件,請重寫 < SPAN style="LINE-HEIGHT: 18px">OnEventName
2.之上的方法和事件基本在自己創建控件時都可以重寫 System.Web.UI.Control的abstract方法,但可以看出以下幾個除外:LoadPostData 和 RaisePostDataChangedEvent 是 IPostBackDataHandler 接口的方法,而 RaisePostBackEvent 屬於 IPostBackEventHandler 接口。如果控件參與回發數據處理,則必須實現 IPostBackDataHandler。如果控件收到回發事件,則必須實現 IPostBackEventHandler 。
3.以上沒有列出 CreateChildControls 方法,這是由於每當 ASP.NET 頁框架需要創建控件樹時就會調用該方法,且該方法調用並不限於控件生命週期的特定階段。例如,可以在加載頁時、在綁定數據過程中或者在呈現過程中調用 CreateChildControls,以及在動態創建控件的時候也可以調用的。
下面給個自己創建個WebPart控件示範,功能比較簡單,在頁面上面實現Ifame實現內嵌頁面的效果。
view plaincopy to clipboardprint?
- using System;
- using System.Collections.Generic;
- using System.Text;
- using System.Web;
- using System.Web.UI;
- using System.Web.UI.HtmlControls;
- using System.Web.UI.WebControls.WebParts;
- < /LI>
- namespace IframeWebpart
- {
- public class Iframe:WebPart,INamingContainer
- {
- private int _width = 100;
- ; private int _height = 100;
- private HtmlTableCell _htc = new HtmlTableCell(); ;
- ; private string _url = @" http://www.baidu.com"; < /SPAN> Request 實例包括的任何回發數據,如果需要在 Load 事件之前對頁或控件執行處理,請使用該事件。
- ; [WebBrowsable(true),Personalizable(true)]
- public int IWidth
- ; {
- get { return _width ; }
- ; set< /SPAN> { _width = value; }
- }
- < /LI>
- [WebBrowsable(true),Personalizable(true)]
- ; public int IHeight
- {
- ; get< /SPAN> { return _height; }
- set { _height = value; }
- ; }
- ; [WebBrowsable(true), Personalizable(true)]
- public string Url
- ; {
- get { return _url ; }
- ; set< /SPAN> { _url = value; }
- }
- < /LI>
- protected override void CreateChildControls( )
- ; {
- //base.CreateChildControls();< SPAN style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 18px; BORDER-RIGHT-STYLE: none; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; BORDER- TOP-STYLE: none; COLOR: black; BORDER-LEFT-STYLE: none; PADDING-TOP: 0px">
- ; this< /SPAN>.Controls.Add(new LiteralControl("<table>"+"/n"));
- this.Controls.Add(new LiteralControl ("<td>"+"/ n"));
- ; this< /SPAN>.Controls.Add(this._htc);
- this.Controls.Add(new LiteralControl ("</td>"+" /n"));
- ; this< /SPAN>.Controls.Add(new LiteralControl("</table>"+"/n")); < /SPAN>
- if (!this.Page.IsPostBack )
- ; {
- this.AddControls(); < /LI>
- ; }
- ; }
- ; protected override void OnLoad(EventArgs e)
- {
- ; base< /SPAN>.OnLoad(e);
- //if (!this.Page.IsPostBack)< /SPAN>
- ; //{< SPAN style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 18px; BORDER-RIGHT-STYLE: none; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; BORDER- TOP-STYLE: none; COLOR: black; BORDER-LEFT-STYLE: none; PADDING-TOP: 0px">
- // this.AddControls ();
- ; //}< SPAN style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 18px; BORDER-RIGHT-STYLE: none; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; BORDER- TOP-STYLE: none; COLOR: black; BORDER-LEFT-STYLE: none; PADDING-TOP: 0px">
- }
- < /LI>
- protected override void Render( HtmlTextWriter writer)
- ; {
- base.Render(writer);
- ; }
- ; private void AddControls()
- {
- ; this< /SPAN>._htc.Controls.Add(new LiteralControl("<iframe id='iframe1' src=""+this.Url+"" mce_src=""+this.Url+"" width='"+this.IWidth+< /SPAN>"' height='"+this.IHeight+"' marginheight='0' frameborder='0' scrolling='no' vspace='0' hspace='0' marginwidth='0' >"+"/n"));
Control類的比較簡單,下面介紹Page類,Page類的生命週期相對control要稍微複雜一些。 (下面介紹參考了 MSDN上面的介紹)
普通的常規頁面生命週期有:頁請求,開始階段,初始化,加載,驗證,回發事件處理,呈現,卸載,除了頁生命週期階段以外,在請求前後還存在應用程序階段,但是這些階段並不特定於頁。以下詳細介紹;
1、頁請求:發生在頁面生命週期之前,用戶請求頁時,ASP.NET將確定是否需要分析和編譯頁,從而確定是否開始頁面的生命週期,或者是否可以在不運行頁的情況下發送頁面緩存以進行響應。
2、開始:設置頁屬性,如:HttpContext以及其他屬性;在此階段,頁面需要確定是回發請求還是新請求,並設置IsPostBack屬性;設置頁面的UICulture屬性。
3、頁面初始化:加載所有主題;控件生成,並設置UniqueID;
注:ViewState、ControlState中的值還未加載至控件;如果頁面是回發,則回發數據也還未加載;故此時控件可以訪問,但值可能出錯。
4、加載:如果當前請求是回發請求,則為控件加載ViewState和ControlState中的值。
5、驗證:調用所有驗證程序控件的Validate方法,此方法將設置驗證程序控件和頁的IsValid屬性。
6、回發事件處理:如果請求是回發請求,則調用所有事件處理程序。
7、呈現:首先對該頁和所有控件進行保存視圖狀態,然後對每個控件調用Render方法,它會提供一個文本編寫器,用於將控件的輸入寫入頁的Response屬性的OutputStream< /SPAN>中。
8、卸載:完成呈現,並已將頁發送至客戶端、準備丟棄該頁後,調用卸載。將卸載屬性如:Response和Request等等,故之後如果在調用這些對象,都將出錯異常的。
在介紹Asp.net之前先來看兩張關於頁面的請求過程圖片:
第一張是個普通的頁面請求過程
第二張是個是個添加的Button按鈕控件PostBack後的頁面請求過程。
比較這兩張圖片可以很清晰的得出以下兩點內容。
一. Asp.Net的頁面生命週期,基本都有如下幾個階段;
< SPAN style="LINE-HEIGHT: 18px; FONT-FAMILY: Century; FONT-SIZE: 9pt">PreInit,
< SPAN style="LINE-HEIGHT: 18px; FONT-FAMILY: Century; FONT-SIZE: 9pt">Init,
< SPAN style="LINE-HEIGHT: 18px; FONT-FAMILY: Century; FONT-SIZE: 9pt">InitComplete,
< SPAN style="LINE-HEIGHT: 18px; FONT-FAMILY: Century; FONT-SIZE: 9pt">PreLoad,
< SPAN style="LINE-HEIGHT: 18px; FONT-FAMILY: Century; FONT-SIZE: 9pt">Load,
< SPAN style="LINE-HEIGHT: 18px; FONT-FAMILY: Century; FONT-SIZE: 9pt">LoadComplete,
< SPAN style="LINE-HEIGHT: 18px; FONT-FAMILY: Century; FONT-SIZE: 9pt">PreRender,
< SPAN style="LINE-HEIGHT: 18px; FONT-FAMILY: Century; FONT-SIZE: 9pt">Render,
< SPAN style="LINE-HEIGHT: 18px; FONT-FAMILY: Century; FONT-SIZE: 9pt">RenderComplete,
< SPAN style="LINE-HEIGHT: 18px; FONT-FAMILY: Century; FONT-SIZE: 9pt">SaveState,
< SPAN style="LINE-HEIGHT: 18px; FONT-FAMILY: Century; FONT-SIZE: 9pt">SaveStateComplete,
< SPAN style="LINE-HEIGHT: 18px; FONT-FAMILY: Century; FONT-SIZE: 9pt">Render,
< SPAN style="LINE-HEIGHT: 18px; FONT-FAMILY: Century; FONT-SIZE: 9pt">UnLoad,
二. 在頁面的響應生命週期階段,控件的生命週期階段也在相應同步進行著。
下面也來個粗略的介紹;
1. PreInit階段
此階段引發PreInit事件,執行OnPreInit()方法,完成的操作,有比如查IsPostBack屬性來確定是
Theme屬性,讀取或設置配置文件屬性。注:如果請求是回發請求,則控件的值尚未從視圖狀態恢復,即:不應該在此事件中設置控件屬性。
2. Init階段
在所有控件都已初始化,且應用了外觀後由里到外引發 Init事件,執行由自Control類繼承得 OnInit()方法,使用該事件來讀取或初始化控件屬性。
3. InitComplete階段
由Page對象引發。使用該事件來處理要求先完成所有初始化工作的任務。執行OnInitComplete()方法。
4. PreLoad< /STRONG>
在 Page 引發該事件後,執行OnPreLoad(),< SPAN style="LINE-HEIGHT: 18px; FONT-FAMILY: 新宋體; FONT-SIZE: 9pt">它會為自身和所有控件加載視圖狀態,然後會處理
5. Load< /STRONG>
Page在Page上調用OnLoad事件方法,然後以遞歸方式對每個子控件執行相同操作,如此循環往復,直到加載完本頁和所有控件為止。使用OnLoad()事件方法來設置控件中的屬性並建立數據庫連接。
6. 控件事件
使用這些事件來處理特定控件事件,如 Button 控件的 Click< /SPAN> 事件等。注:在回發請求中,如果頁包含驗證程序控件,請在執行任何處理之前檢查 Page和各個驗證控件的IsValid屬性。 開發自定義 ASP.NET ;服務器控件。用戶
7. LoadComplete
引發LoadComplete事件,執行OnLoadComplete()方法,對需要加載頁上的所有其他控件的任務使用該事件。
8. PreRender
引發PreRender事件,執行由Control類繼承來的OnPreRender()方法,使用該事件對頁或其控件的內容進行最後更改。注:在該事件發生前的操作:Page對所有控件遞歸進行EnsureChildControl操作設置了DataSourceID屬性的數據綁定控件會調用DataBind方法。
9. PreRenderComplete< /STRONG>
引發 PreRenderComplete事件,執行的On PreRenderComplete方法。
10. SaveStateComplete
引發SaveStateComplete事件,執行由OnSaveStateComplete(EventArgs e)方法,在該事件發生前,已針對頁和所有控件保存了ViewState。將忽略此時對頁或控件進行的任何更改。 。
11. Render
這不是事件;在處理的這個階段,Page 對象會在每個控件上調用此方法。所有 ASP.NET Web 服務器控件都有一個用於寫出發送給瀏覽器的控件標記的 Render 方法。如果創建自定義控件,通常要重寫此
方法以輸出控件的標記。不過,如果自定義控件只合併標準的 ASP.NET Web < SPAN style="LINE-HEIGHT: 18px; FONT-FAMILY: 新宋體; FONT-SIZE: 9pt">服務器控件,不合併自定
義標記,則不需要重寫 Render < SPAN style="LINE-HEIGHT: 18px; FONT-FAMILY: 新宋體; FONT-SIZE: 9pt">方法。有關更多信息,請參見
控件(.ascx 文件)自動合併呈現,因此不需要在代碼中顯式呈現該控件
12.Unload
引發Unload事件,執行由Control類繼承來的OnUnLoad()方法,對子控件由里向外執行此方法。
注意點。當從 Page ;類繼承類時,除了可以處理由頁引發的事件以外,還可以重寫頁的基類中的方法。例如,可以重寫頁的 < SPAN style="LINE-HEIGHT: 18px; COLOR: #000000">InitializeCulture
以上就是本人的理解和總結,比較簡單,可以說只有的理解了簡單Control類和Page類的通用生命週期,以後才可以分析複雜的問題,比如動態創建控件時的數據丟失問題等。再比如目前有不少人喜歡在開發Web時候,比較喜歡直接讓新建的Page頁面繼承自Page類,在這個繼承來里面來寫一些實現用戶身份驗證功能,但是如果對頁面的生命週期比較了解後,可以這樣來做,首先寫個繼承自Page類的類,在這個類裡面根據需要來重寫abstract方法和寫一些通用的用戶身份驗證方法,之後讓自己的頁面後台代碼類來實現這個類,從而達到通用的效果。這樣做對代碼復用性的提高,大家覺得是否有幫助呢?
以上拙見,如有錯誤之處,請幫忙指教。
參考資料
1. MSDN文檔ASP.NET 頁生命週期概述
2. 《庖丁解牛:縱向切入ASP.NET 3.5控件和組件開發技術》
3. 《Programming ASP.NET 》學習筆記(控件)
4. ; SnowQuery的專欄
5. ; 深入理解Asp.net動態控件
- this._htc.Controls.Add(new ;LiteralControl("</iframe>"+"/n"));
- ; }
- }
- }
- < /OL>