TrackViewState到底是干什么的
在ASP.NET自定義控件開發中,如果需要保存控件的狀態,通常都需要實現SaveViewState(),LoadViewState()和TrackViewState()三個方法,這是由IStateManager接口所定義的。
前兩個方法作用很明晰,SaveViewState()是將控件的當前狀態抽取為一個狀態對象,頁面類獲取所有控件的狀態對象對其進行編碼生成可在網絡上傳輸的格式(Base64),并將其塞入到一個id為__VIEWSTATE的input元素中發給瀏覽器。LoadViewState()是控件從瀏覽器中傳回來的數據中重新讀取值,使其回復到上次狀態。
那么TrackViewState(),它是干什么的?
它怎么就讓控件跟蹤視圖狀態的變化了?瀏覽器回發的數據包含了頁面當前控件的值,而__VIEWSTATE隱藏域中包含了上次控件的狀態,兩者一比不就知道哪些變化了,用你TrackViewState()是干什么?原來,為了減少在網絡上的傳輸量,應該只保存“變化”的數據到視圖狀態中。所有Web控件大都派生自Control類,Control類有一個ViewState屬性,它是一個StateBag類的對象。控件可以有多個屬性,每個屬性都有一個值,StateBag對象按照“Key-Item”格式管理這些數據,一般將屬性名當作Key。Item則封裝了對應屬性的值(注意它是一個StateItem類型的對象)。
StateBag對象保存的Item有一個IsDirty屬性用于標識此Item是否有更改。為此,StateBag對象設置了一個標記,當此標記為true時,在給Item賦值時就會同步設置這一Item的IsDirty屬性,通告外界——我的數據有變化。而這個內部標記就可以通過TrackView()方法進行設置。如果不設置這個內部標記,那么,不管怎樣修改StateBag中的Item,這一Item其IsDirty屬性始終都是false。
可以設計一個簡單的網頁,然后用Reflector查看其生成的程序集源碼。
檢看StateBag的源碼,發現它有以下的代碼說明這個內部標記名為marked.
private bool marked;
以下為StateBag的TrackViewState()的反匯編代碼:
internal void TrackViewState()
{
??? this.marked = true;
}
向ViewState中追加數據的方法本質上是通過StateBag的Add()方法實現的:
public StateItem Add(string key, object value)
{
?//Key不能為空
??? if (string.IsNullOrEmpty(key))
??? {
??????? throw ExceptionUtil.ParameterNullOrEmpty("key");
??? }
??? //根據Key查找集合中的數據對象
??? StateItem item = this.bag[key] as StateItem;
??? if (item == null)? //沒有找到對應的數據對象
??? {
??????? //如果傳入的value不為空或要求跟蹤,則創建一個對象加入到集合中
??????? if ((value != null) || this.marked)
??????? {
??????????? item = new StateItem(value);
??????????? this.bag.Add(key, item);
??????? }
??? }
??? else?
???? //如果找到對應的數據對象
????????? if ((value == null) && !this.marked)
????????? {
????????????? this.bag.Remove(key); //值為空,且不要求跟蹤,則從集合中移除此對象
?????????? }
????????? else? //要求跟蹤或者值不為空,設置對應的數據對象值
????????? {
????????????? item.Value = value;
?????????? }
?//設定已更改標記
??? if ((item != null) && this.marked)
??? {
??????? item.IsDirty = true;
??? }
??? return item;
}
可以清楚地看到,如果標記被設置,IsDirty屬性就返回true,否則,保持為false.
注意:這里并沒有比對原始值和傳入的值是否相等再設置IsDirty屬性。因此,只要標記被設置,任何對視圖狀態的非空賦值都被認為是Dirty的。
?
因此,TrackView()方法其實就是設置了一個“請監控我的變化”的標記,調用此方法之后,任何對控件屬性的改變都會被跟蹤,這樣一來,此控件的SaveViewState()方法在生成狀態對象時就會將此屬性的修改記錄下來。簡單地說:只有IsDirty=true的屬性值才會被SaveViewState()方法處理。這就避免了為控件所有的屬性都生成狀態數據,大大減少了要保存的數據量。
以下為StateBag類的SaveViewState()反匯編代碼,可以清楚地看到其中使用了IsDirty屬性。
internal object SaveViewState()
{
??? ArrayList list = null;
??? if (this.bag.Count != 0)
??? {
??????? IDictionaryEnumerator enumerator = this.bag.GetEnumerator();
??????? while (enumerator.MoveNext())
??????? {
??????????? StateItem item = (StateItem) enumerator.Value;
??????????? if (item.IsDirty)
??????????? {
??????????????? if (list == null)
??????????????? {
??????????????????? list = new ArrayList();
??????????????? }
??????????????? list.Add(new IndexedString((string) enumerator.Key));
??????????????? list.Add(item.Value);
??????????? }
??????? }
??? }
??? return list;
}
一切都清楚了。
那么,到底控件的視圖狀態是怎樣保存的?這涉及到頁面的生命周期。
當頁面被裝載時,它的ProcessRequest()方法被調用。在此方法中,會調用一個SaveAllState方法,此方法內部又調用SaveViewStateRecursive()方法(來自基類Control),
SaveViewStateRecursive()先調用Control.SaveViewState()保存自己的數據,再遞歸地調用每個子控件的SaveViewStateRecursive()方法獲取所有子控件的狀態對象,然后一級級返回,最終得到整個頁面的狀態對象,緊接著將這一對象按Base64編碼生成頁面視圖狀態字串并放入到__VIEWSTATE隱藏域中(由Page.SavePageStateToPersistenceMedium方法完成)。
綜上所述:
如果控件沒調用TrackView()方法,那么,本次對控件屬性的修改將不會被添加到__VIEWSTATE隱藏域中,因此,下次頁面回發時,控件的屬性將回復為默認值。
理解這點還是有意義的,特別是在動態創建控件的情況下,請看以下這個典型示例:
?protected void Page_Load(object sender, EventArgs e)
??? {
??????
??????? CheckBoxList chk = new CheckBoxList();
??????? if (!IsPostBack)
??????? {
??????????? chk.Items.Add("Hello");
??????? }
??????? form1.Controls.Add(chk);
??? }
?在Page上扔個Button,以便可以PostBack。運行后Postback的結果,“Hello” item沒被保留。
改為:
protected void Page_Load(object sender, EventArgs e)
??? {
??????
??????? CheckBoxList chk = new CheckBoxList();
??????? form1.Controls.Add(chk);
??????? if (!IsPostBack)
??????? {
??????????? chk.Items.Add("Hello");
??????? }
??????
??? }
或者:
?protected void Page_Load(object sender, EventArgs e)
??? {
??????
??????? CheckBoxList chk = new CheckBoxList();
??????? (chk.Items as IStateManager).TrackViewState();
??????? if (!IsPostBack)
??????? {
??????????? chk.Items.Add("Hello");
??????? }
??????? form1.Controls.Add(chk);
??? }
都可以在多次回發時保證Hello項出現,并正確地恢復它的狀態(選中還是不選中)。
這個例子說明:將控件加入到頁面類的Controls集合中時,會自動調用TrackViewState()方法。
注意:Control.TrackViewState()方法是保護的,不允許外界調用。而動態創建Button等簡單控件時,沒有辦法在頁面類中直接調用TrackViewState()方法。因此,通過Controls.Add()方法間接調用TrackViewState()方法是唯一的選擇。
最后給出一條動態創建控件的原則:
應該在new出控件對象之后,馬上將其加入到父控件的Controls集合中,這樣可以完全地保證它的狀態能在回發時恢復。
轉載于:https://www.cnblogs.com/jsping/archive/2012/09/17/2688223.html
總結
以上是生活随笔為你收集整理的TrackViewState到底是干什么的的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 文件夹操作
- 下一篇: Xcode 卸载方法