ASP.NET控件開(kāi)發(fā)基礎(chǔ)之自定義視圖狀態(tài)管理
ASP.NET控件開(kāi)發(fā)基礎(chǔ)之自定義視圖狀態(tài)管理是如何建立的呢?剛開(kāi)篇的時(shí)后在最后把屬性值用視圖狀態(tài)來(lái)保存時(shí),得以把當(dāng)前狀態(tài)保存下來(lái),關(guān)于視圖狀態(tài)的概述,這里不再累贅,沒(méi)了解過(guò)的朋友可以在MSDN里輸入視圖狀態(tài)概述了解一下.以下我們還是以以前講過(guò)的內(nèi)容為例,一起繼續(xù)來(lái)改善控件的使用。
ASP.NET控件開(kāi)發(fā)基礎(chǔ)之自定義視圖狀態(tài)管理示例一
我們啟用了跟蹤,按下確定按鈕后,控件屬性發(fā)生變化,按下無(wú)事件按鈕后,控件狀態(tài)則恢復(fù)到之前的狀態(tài),而且在跟蹤狀態(tài)下發(fā)現(xiàn)Custom無(wú)視圖狀態(tài).
- ﹤%@ Page Language="C#" Trace="true" %﹥
- ﹤%@ Register Assembly="CustomComponents" Namespace="CustomComponents" TagPrefix="custom" %﹥
- ﹤!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"﹥
- ﹤script runat="server"﹥
- protected void Button1_Click(object sender, EventArgs e)
- {
- Custom1.Age = 21;
- Custom1.CustomMetier = Metier.教師;
- Custom1.CustomAddress.City = "杭州";
- Custom1.CustomAddress.State = "中國(guó)";
- Custom1.CustomAddress.Street = "街道";
- Custom1.CustomAddress.Zip = "310000";
- }
- ﹤/script﹥
- ﹤html xmlns="http://www.w3.org/1999/xhtml" ﹥
- ﹤head runat="server"﹥
- ﹤title﹥無(wú)標(biāo)題頁(yè)﹤/title﹥
- ﹤/head﹥
- ﹤body﹥
- ﹤form id="form1" runat="server"﹥
- ﹤div﹥
- ﹤custom:Custom ID="Custom1" runat="server"﹥
- ﹤/custom:Custom﹥
- ﹤br /﹥
- ﹤br /﹥
- ﹤asp:Button ID="Button1" runat="server" OnClick="Button1_Click" Text="確定" /﹥
-
- ﹤asp:Button ID="Button2" runat="server" Text="無(wú)事件" /﹥
- ﹤/div﹥
- ﹤/form﹥
- ﹤/body﹥
- ﹤/html﹥
那么接下來(lái)將修改Custom的屬性更改為視圖狀態(tài)保存,代碼如下
重新編譯一下代碼,再次測(cè)試上面代碼Custom的Age和CustomMetier屬性可以保存其狀態(tài),而無(wú)法保存CustomAddress這個(gè)復(fù)雜屬性的狀態(tài)值.這個(gè)也可以理解,我們沒(méi)有為CustomAddress的子屬性值保存在視圖狀態(tài)里.啟動(dòng)跟蹤后,還發(fā)現(xiàn)Custom控件在更改控件屬性后保存了一部分的視圖狀態(tài).
- #region 屬性
- [Description("年齡")]
- public int Age
- {
- get { return ViewState["Age"] != null ? (int)ViewState["Age"] : 0; }
- set { ViewState["Age"] = value; }
- }
- [Description("姓名")]
- public String Name
- {
- get { return ViewState["Name"] != null ? (string)ViewState["Name"] : string.Empty; }
- set { ViewState["Name"] = value; }
- }
- [TypeConverter(typeof(GameConverter))]
- [Description("喜歡的游戲")]
- public String Game
- {
- get { return ViewState["Game"] != null ? (string)ViewState["Game"] : string.Empty; }
- set { ViewState["Game"] = value; }
- }
- [Description("職業(yè)")]
- public Metier CustomMetier
- {
- get { return ViewState["CustomMetier"] != null ? (Metier)ViewState["CustomMetier"] : Metier.程序員; }
- set { ViewState["CustomMetier"] = value; }
- }
- #endregion
接下來(lái)我們更改Address的字屬性,把其值保存在視圖狀態(tài)下.
ASP.NET控件開(kāi)發(fā)基礎(chǔ)之自定義視圖狀態(tài)管理代碼如下:
- #region 屬性
- [
- Category("Behavior"),
- DefaultValue(""),
- Description("街道"),
- NotifyParentProperty(true),
- ]
- public String Street
- {
- get { return ViewState["Street"] != null ? (string)ViewState["Street"] : String.Empty; }
- set { ViewState["Street"] = value; }
- }
- [
- Category("Behavior"),
- DefaultValue(""),
- Description("城市"),
- NotifyParentProperty(true),
- ]
- public String City
- {
- get { return ViewState["City"] != null ? (string)ViewState["City"] : String.Empty; }
- set { ViewState["City"] = value; }
- }
- [
- Category("Behavior"),
- DefaultValue(""),
- Description("國(guó)籍"),
- NotifyParentProperty(true),
- ]
- public String State
- {
- get { return ViewState["State"] != null ? (string)ViewState["State"] : String.Empty; }
- set { ViewState["State"] = value; }
- }
- [
- Category("Behavior"),
- DefaultValue(""),
- Description("郵編"),
- NotifyParentProperty(true)
- ]
- public String Zip
- {
- get { return ViewState["Zip"] != null ? (string)ViewState["Zip"] : String.Empty; }
- set { ViewState["Zip"] = value; }
- }
- #endregion
重新編譯后,發(fā)現(xiàn)問(wèn)題了,編譯不通過(guò),當(dāng)前上下文不存在名稱ViewState.如果這些屬性直接定義在Custom控件下則一點(diǎn)問(wèn)題也沒(méi)有,但下面定義的是Address復(fù)雜屬性的子屬性.而Address屬性又不能繼承Control類,所以我們需要自定義一個(gè)ViewState屬性
ASP.NET控件開(kāi)發(fā)基礎(chǔ)之自定義視圖狀態(tài)管理如下代碼:
- private bool _isTrackingViewState;
- private StateBag _viewState;
- protected StateBag ViewState
- {
- get
- {
- if (_viewState == null)
- {
- _viewState = new StateBag(false);
- if (_isTrackingViewState) ((IStateManager)_viewState).TrackViewState();
- }
- return _viewState;
- }
- }
先定義兩個(gè)變量,然后定義一個(gè)ViewState屬性,ViewState類型本身便是一個(gè)StateBag類型.大家一定注意到了 IStateManager接口,下面還有一個(gè)TrackViewState方法.先不管他.重新編譯下,編譯通過(guò),重新測(cè)試下,發(fā)現(xiàn)還是沒(méi)有變化.
MSDN上對(duì)ViewState能保存的值已經(jīng)講的很清楚了.你可以保存一些簡(jiǎn)單類型,但無(wú)法保存自定義類型,而我們定義的Address就是一個(gè)自定義類型.為保存自定義類型數(shù)據(jù),所以我們需要自定義類型狀態(tài)管理
自定義類型狀態(tài)管理,那么我們就必須接觸到IStateManager這個(gè)接口,此接口有一個(gè)屬性和三個(gè)方法,如下
所以Address要繼承IStateManager接口,并顯示實(shí)現(xiàn)接口屬性和方法,注意是顯示實(shí)現(xiàn) .
下面看Address類具體的自定義狀態(tài)管理代碼
- #region
- bool IStateManager.IsTrackingViewState
- {
- get
- {
- return _isTrackingViewState;
- }
- }
- void IStateManager.LoadViewState(object savedState)
- {
- if (savedState != null)
- {
- ((IStateManager)ViewState).LoadViewState(savedState);
- }
- }
- object IStateManager.SaveViewState()
- {
- object savedState = null;
- if (_viewState != null)
- {
- savedState =
- ((IStateManager)_viewState).SaveViewState();
- }
- return savedState;
- }
- void IStateManager.TrackViewState()
- {
- _isTrackingViewState = true;
- if (_viewState != null)
- {
- ((IStateManager)_viewState).TrackViewState();
- }
- }
- #endregion
理解控件自定義的狀態(tài)管理,你有必要了解控件的生命周期,了解控件生命周期,那問(wèn)題就迎刃而解了.
大家可以翻閱MSND的控件執(zhí)行生命周期
我個(gè)人認(rèn)為最好的理解方法就是為上面代碼設(shè)置三個(gè)斷點(diǎn), 如下圖
好了,下面把我們測(cè)試的那個(gè)aspx頁(yè)面設(shè)置為起始頁(yè),然后按F5,開(kāi)始測(cè)試.
本該啟動(dòng)后跳到TrackViewState方法里,但沒(méi)跳進(jìn)來(lái),好怪,而且自定義類型狀態(tài)管理后頁(yè)面并未保存其值.
讓我們回到Custom類里,我們還需要為屬性(復(fù)雜屬性)定義狀態(tài)管理.
本身Control也有一套默認(rèn)的狀態(tài)管理機(jī)制,而沒(méi)有實(shí)現(xiàn)IStateManager接口 ,
ASP.NET控件開(kāi)發(fā)基礎(chǔ)之自定義視圖狀態(tài)管理其實(shí)現(xiàn)如下:
對(duì)下面代碼我認(rèn)為是錯(cuò)誤的,因?yàn)闀?shū)上全是這么寫(xiě)的,我認(rèn)為因先把_viewState顯示轉(zhuǎn)換為IStateManager類型,
因?yàn)镾tateBag本身是繼承IStateManager接口,但MSDN中,我并沒(méi)看到其實(shí)現(xiàn)IStateManager的方法,而是顯示的實(shí)現(xiàn),當(dāng)我用反射機(jī)制查看其方法時(shí),卻又發(fā)現(xiàn)是有其方法的,但當(dāng)你不把StateBag顯示轉(zhuǎn)換為IStateManager類型,而直接調(diào)用下面方法時(shí),將會(huì)出錯(cuò).如果書(shū)上是對(duì)的,還請(qǐng)看到此文的人指點(diǎn)一下,對(duì)此我已經(jīng)疑惑很長(zhǎng)時(shí)間了. 如果我是對(duì)的,那下面的_viewState因先顯示轉(zhuǎn)換為IStateManager類型,事實(shí)上我們都是這么做的.
- private StateBag _viewState;
- protected virtual StateBag ViewState{
- get {
- if(_viewState != null)
- {
- return _viewState;
- }
- _viewState = new StateBag(ViewStateIgnoresCase);
- if(IsTrackingViewState)
- _viewState.TrackViewState();
- return _viewState;
- }
- }
- protected virtual void TrackViewState(){
- if(_viewState != null) {
- _viewState.TrackViewState();
- }
- return null;
- }
- protected virtual object SaveViewState(){
- if(_viewState != null) {
- _viewState.SaveViewState();
- }
- return null;
- }
- protected virtual void LoadViewState(object savedState){
- if(savedState != null) {
- ViewState.LoadViewState(savedState);
- }
- }
下面再看如何在Custom類中自定義屬性狀態(tài)管理,當(dāng)你定義了復(fù)雜類型時(shí),你就需要重寫(xiě)上面的幾個(gè)方法.
ASP.NET控件開(kāi)發(fā)基礎(chǔ)之自定義視圖狀態(tài)管理具體代碼如下:
首先我們對(duì)屬性進(jìn)行視圖狀態(tài)的跟蹤,然后重寫(xiě)了Control類的三個(gè)方法.其一方面調(diào)用了基類方法,一方面調(diào)用了Addres類的顯示接口方法.
Pair類為一個(gè)輔助類,用作存儲(chǔ)兩個(gè)相關(guān)對(duì)象的基本結(jié)構(gòu),下面根據(jù)調(diào)試結(jié)果來(lái)理解.在Custom類中對(duì)其三個(gè)方法設(shè)置斷點(diǎn).
- public Address CustomAddress
- {
- get
- {
- if (address == null)
- {
- address = new Address();
- if (IsTrackingViewState)
- {
- ((IStateManager)address).TrackViewState();
- }
- }
- return address;
- }
- }
- #region
- protected override void LoadViewState(object savedState)
- {
- Pair p = savedState as Pair;
- if (p != null)
- {
- base.LoadViewState(p.First);
- ((IStateManager)CustomAddress).LoadViewState(p.Second);
- return;
- }
- base.LoadViewState(savedState);
- }
- protected override object SaveViewState()
- {
- object baseState = base.SaveViewState();
- object thisState = null;
- if (address != null)
- {
- thisState = ((IStateManager)address).SaveViewState();
- }
- if (thisState != null)
- {
- return new Pair(baseState, thisState);
- }
- else
- {
- return baseState;
- }
- }
- protected override void TrackViewState()
- {
- if (address != null)
- {
- ((IStateManager)address).TrackViewState();
- }
- base.TrackViewState();
- }
- #endregion
設(shè)置斷點(diǎn)以后,啟動(dòng)起始頁(yè)開(kāi)始測(cè)試.
啟動(dòng)后第一步將會(huì)跳到Custom類的TrackViewState方法里面,執(zhí)行完此方法后IsTrackingViewState將設(shè)置為true,
從而可以繼續(xù)調(diào)用address的TrackViewState方法,另外可以看到address屬性為空值,然后按F5,通過(guò)此方法繼續(xù)
第二步將會(huì)跳到Custom類的SaveViewState方法里,發(fā)現(xiàn)baseState和thisState均為空,直接執(zhí)行基類方法.按F5繼續(xù)
第三步將會(huì)跳到Address類的TrackViewState方法里,_isTrackingViewState初始化時(shí)為false,執(zhí)行此方法后將賦值為ture,然后調(diào)用_viewState的TrackViewState方法.
初始化的工作就完成了,然后我們點(diǎn)擊確定按鈕,重新執(zhí)行.
重新跳到Custom類的TrackViewState方法里,步驟跟上面第一步一樣,按F5,繼續(xù)
跳到Address類的TrackViewState方法里,步驟跟上面第二步一樣,按F5繼續(xù)
跳到Custom類的SaveViewState方法里,此時(shí)address不再為null,此時(shí)會(huì)返回Pair構(gòu)造函數(shù).
然后會(huì)跳到Address類SaveViewState方法里,接著會(huì)跳回來(lái),再執(zhí)行Custom類的SaveViewState方法
以上調(diào)試方法不一定正確,但多調(diào)用會(huì)理解的更深刻.
我們還發(fā)現(xiàn)并未跳到LoadViewState方法里,以前的主要工作就是保存視圖狀態(tài)更改,接下來(lái)再次調(diào)試的話,就會(huì)跳到LoadViewState方法方法里面,這時(shí)你會(huì)發(fā)現(xiàn)savedState就是SaveViewState方法中保存下來(lái)的視圖狀態(tài),可以看到其first和second值分別為Custom的頁(yè)面屬性和Address這個(gè)復(fù)雜屬性的值.
視圖狀態(tài)以鍵/值的方式保存,有一個(gè)屬性為Dirty,表示StateItem是否被修改過(guò),可以通過(guò)SetDirty方法和SetItemDirty方法給StateItem添加Dirty標(biāo)記.
- internal void SetDirty()
- {
- _viewState.SetDirty(true);
- }
如果此StateItem標(biāo)記為Dirty的話,則在SaveViewState方法中以鍵/值的方式保存到ArrayList中.
SaveViewState方法和LoadViewState方法執(zhí)行的是相反的操作.我們?cè)陧?yè)面上看到的值,總是LoadViewState方法反序列化視圖狀態(tài).大家可以具體去了解StateBag類默認(rèn)情況下SaveViewState方法和LoadViewState方法的實(shí)現(xiàn)過(guò)程.
當(dāng)控件禁用視圖狀態(tài)時(shí)將不再執(zhí)行SaveViewState和LoadViewState,可以去調(diào)試一下就知道了.
還需要注意的是,我們了解視圖狀態(tài)可以保存的類型,其也是同過(guò)類型轉(zhuǎn)換器來(lái)轉(zhuǎn)換此類型,否則的話將以二進(jìn)制串行化功能來(lái)串行化數(shù)值得,這樣降低了效率,所以我們還需要為其定義一個(gè)類型轉(zhuǎn)換器,第九篇的時(shí)候已經(jīng)講過(guò)怎么定義了,這里就不列代碼了,只是需要注意就是.
此外ASP.NET中加入了控件狀態(tài),因?yàn)橐晥D狀態(tài)要么全開(kāi),要么全禁用,控件狀態(tài)則是為彌補(bǔ)這一點(diǎn),大家可以看MSDN,也可參考相關(guān)文章.
asp.net2.0中還可以對(duì)視圖狀態(tài)進(jìn)行分塊處理,你需要在web.config里如下設(shè)置
- ﹤system.web﹥
- ﹤pages maxPageStateFieldLength="1000" ﹥
- ﹤system.web﹥
ASP.NET還加入了視圖狀態(tài)持久性機(jī)制,大家可以在博客園參考相關(guān)文章,這里就當(dāng)了解下有這種機(jī)制存在.
好了,就寫(xiě)到這里,個(gè)人認(rèn)為視圖狀態(tài)是很重要的,下面很多東西都要涉及到它,所以要好好理解這個(gè)東西.
寫(xiě)的比較亂,對(duì)視圖狀態(tài)我真的比較敏感,很難理解,也難表達(dá),可能很多地方寫(xiě)錯(cuò),還請(qǐng)指出,這樣才能提高。
ASP.NET控件開(kāi)發(fā)基礎(chǔ)之自定義視圖狀態(tài)管理的相關(guān)內(nèi)容就向大家介紹到這里,希望對(duì)你了解ASP.NET控件開(kāi)發(fā)基礎(chǔ)之自定義視圖狀態(tài)管理有所幫助。
【編輯推薦】




















