在前面的系列文章中,我們曾經介紹了視圖狀態和控件狀態的基本概念和典型應用,從中可以發現,視圖狀態和控件狀態對于自定義服務器控件實現的重要性。本文將繼續這一主題,重點介紹實現視圖狀態和控件狀態自定義管理的方法。
自定義視圖狀態管理
在介紹視圖狀態時,我們曾經提到過:對于簡單屬性,例如,string、int等類型,.net執行引擎將自動啟用默認視圖狀態管理機制,以便完成相應的功能。然而,如果開發人員在viewstate中保存的是自定義數據類型,或者需要實現自定義方式優化視圖狀態管理時,則必須實現自定義視圖狀態管理。
實現自定義視圖狀態管理可以通過兩種方法。方法一:實現system.web.ui命名空間中的istatemanager接口成員,其中包括istrackingviewstate屬性和trackviewstate、saveviewstate和loadviewstate方法。這種方法主要是針對自定義數據類型的視圖狀態管理的情況。方法二:重寫control基類的3個視圖狀態管理方法:trackviewstate、saveviewstate和loadviewstate。這些方法與istatemanager接口定義的3個方法名稱一致。這種方法主要用于通過自定義方式優化默認視圖狀態管理的情況,其主要目的在于提高效率和性能。掌握以上兩種實現方法的捷徑是,必須深刻理解.net框架內部實現視圖狀態管理的過程。下面兩小節內容都是有關內部實現方法的介紹。每一節中均有實現代碼,實際就相當于實例代碼。所有服務器控件的自定義視圖狀態管理的實現都不會偏離那些代碼所表達的邏輯。當讀者真正掌握了那些內部實現方法,那么自定義視圖狀態管理的實現方法也就迎刃而解了。
1、實現基于istatemanager接口的自定義視圖狀態管理
對于復雜屬性而言,多數需要實現自定義視圖狀態管理,其關鍵是實現system.web.ui.istatemanager接口中定義的方法和屬性。下面列舉了istatemanager接口定義代碼。
public interface istatemanager{ bool istrackingviewstate {get;} void loadviewstate(object state); object saveviewstate(); void trackviewstate();}
如上代碼所示,istatemanager接口要求類實現istrackingviewstate屬性,以及loadviewstate、saveviewstate和trackviewstate方法。istrackingviewstate屬性定義,當由類實現時,獲取一個布爾值,通過該值指示服務器控件是否正在跟蹤其視圖狀態更改。如果服務器控件正在跟蹤其視圖狀態更改,則為true;否則為false。saveviewstate方法定義,當由類實現時,將服務器控件的視圖狀態更改保存到object中。loadviewstate方法定義,當由類實現時,加載服務器控件以前保存的控件視圖狀態,其中的參數state表示包含控件保存的視圖狀態值的object。trackviewstate方法定義,當由類實現時,指示服務器控件跟蹤其視圖狀態更改。
viewstate屬性與istatemanager接口之間存在密切聯系。viewstate屬性的類型是statebag類,statebag類通過實現istatemanager接口中定義的方法和屬性來參與狀態管理。其實現過程如下。
public sealed class statebag : istatemanager, idictionary,icollection, ienumerable{
private bool _istrackingviewstate;
private arraylist _keys;
private arraylist _values;
private stateitem _item;
bool istatemanager.istrackingviewstate {
get { return _istrackingviewstate; }
}
void istatemanager.trackviewstate() {
_istrackingviewstate = true;
}
object istatemanager.saveviewstate() {
_keys = new arraylist();
_values = new arraylist();
idictionaryenumerator mydirctionaryenumerator = this.getenumerator();
while(mydictionaryenumerator.movenext()) {
if(this.item[(string)mydictionaryenumerator.key].isdirty) {
_keys.add(mydictionaryenumerator.key);
_values.add(mydictionaryenumerator.value);
}
}
if(_keys.count>0) {
return new pair(_keys,_values);
}
}
void istatemanager.loadviewstate(object savedstate) {
if(savedstate is pair) {
_keys = (arraylist)tempp.first;
_values = (arraylist)tempp.second;
idictionaryenumerator mydirctionaryenumerator = this.getenumerator();
while(mydictionaryenumerator.movenext()) {
for(int j=0;j<_keys.count;j++)
{
if((string)mydictionaryenumerator.key == _keys[j].tostring());
{
this.item[_keys[j].tostring()].value = (object)_values[j];
}
}
}
}
}
}
請讀者注意:以上代碼為示意性代碼,并非嚴格意義上的實現代碼。在此列出,主要是用于說明statebag類實現istatemanager接口的邏輯過程。
通過上面的代碼,我們可以看到:
(1)在istrackingviewstate屬性中,將該屬性設置為只讀,并且使用私有變量_istrackingviewstate。
(2)在trackviewstate方法中,把istrackingviewstate屬性使用的私有變量_istrackingviewstate設置為true,這指示系統當某個stateitem添加到statebag中,或者某個stateitem值被修改時,statebag類就會自動將該stateitem標記為修改過即添加dirty標記。
(3)在saveviewstate方法中,循環statebag中的每個stateitem,如果該stateitem被標記為dirty,那么就將其鍵和值分別添加到兩個arraylist中,并返回該對象。
(4)在loadviewstate方法中,執行了與saveviewstate方法相反的操作。首先將savedstate對象分解為兩個保存有鍵和值的arraylist,然后將其中的值加載到相應的stateitem對象中。
以上就是viewstate屬性實現istatemanager接口的基本過程。所有的視圖狀態管理過程,都要使用以上的實現過程,因此理解以上邏輯對于深入掌握自定義視圖狀態管理機制具有舉足輕重的作用。
2、實現基于control基類的自定義視圖狀態管理
如果開發人員需要優化默認視圖狀態管理機制,以提高控件運行效率和性能,那么必須理解control基類中默認視圖狀態管理機制。通過掌握這個管理機制,可以模仿其處理過程以實現自定義視圖狀態管理。
實現基于control基類的自定義視圖狀態管理,需要開發人員實現3個方法:loadviewstate、saveviewstate和trackviewstate。它們與上一小節中介紹的istatemanager接口成員方法同名,并且在方法意義上也基本相同。在此就不對這3個方法多做說明了。
control基類中的默認視圖狀態管理機制定義了一個statebag類型的viewstate屬性,并將視圖狀態管理的任務委托給它。下面請看control基類的默認狀態管理的實現邏輯。
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);
}
}
從上面的代碼可以看出:viewstate屬性是statebag類型,當_viewstate不為null時,則返回_viewstate;當_viewstate為null時,則初始化一個statebag類型的變量_viewstate,并判斷控件是否正在跟蹤其視圖狀態更改,如果服務器控件正在跟蹤其視圖狀態更改,那么就調用trackviewstate方法開始狀態跟蹤,最后返回_viewstate。另外,在trackviewstate、saveviewstate、loadviewstate方法中,均使用了statebag類中有關視圖狀態管理的方法。
在control基類的默認視圖狀態管理過程中,由于定義了viewstate屬性為statebag類型,所以必然使用上文中statebag類實現視圖狀態管理的邏輯。如果讓control基類實現istatemanager接口中的方法和屬性,那么其實現過程必然與statebag類實現istatemanager接口大同小異,這必然將造成重復,由此可能造成.net框架改變istatemanager接口的訪問性質。此外,在進行自定義視圖狀態管理的過程中,可能出現statebag類型與control基類的視圖狀態管理機制的沖突,那樣就可能產生混亂。而采用目前的這種方式,無論從靈活性、繼承性、可復用性,以致從編程人員的習慣上,都具有很多優點。 自定義控件狀態管理
視圖狀態與控件狀態在數據管理方面不太相同。asp.net 2.0在支持針對簡單屬性的默認視圖狀態管理機制的同時,還支持自定義視圖狀態管理。然而,對于控件狀態管理而言,則不存在默認控件狀態管理機制。開發人員必須實現自定義控件狀態管理過程。本小節介紹一下自定義控件狀態管理的實現方法,這對于實現基于控件狀態功能的對象有著重要意義。
實際上,在前面系列文章介紹控件狀態的過程中,讀者已經接觸了控件狀態實現的內容,其中就包括自定義控件狀態管理的實現。這個過程的實現與基于control基類的自定義視圖狀態管理非常相似,二者都需要重寫control基類中的方法。實現自定義控件狀態管理需要重寫control基類的savecontrolstate和loadcontrolstate。下面列舉了一段簡單示例代碼。
public class sample : control {
private int currentindex = 0;
protected override void oninit(eventargs e) {
page.registerrequirescontrolstate(this);
base.oninit(e);
}
protected override object savecontrolstate() {
return currentindex != 0 ? (object)currentindex : null;
}
protected override void loadcontrolstate(object state) {
if (state != null) { currentindex = (int)state; }
}
}
實現自定義控件狀態管理分為3個關鍵步驟:
(1)調用registerrequirescontrolstate方法。該方法用于將自定義控件注冊為具有持久性控件狀態的控件。
(2)重寫savecontrolstate方法。該方法用于保存自頁回發到服務器后發生的任何服務器控件狀態更改。
(3)重寫loadcontrolstate方法。該方法用于從savecontrolstate方法保存的上一個頁請求還原控件狀態信息。
小結
本章主要介紹了自定義視圖狀態和控件狀態管理的內容。它們對于實現自定義服務器控件有著重要意義。建議讀者首先深入理解理論知識,然后再嘗試進行開發工作,這樣可獲得更好的學習效果。