Control.SaveViewState是如何保存视图信息的
我们平时所见到的web服务器控件,一般而言,其直接的基类,不是WebControl就是Control,我们今天说说Control的SaveViewState方法,这个方法的源码如下:
private StateBag _viewState;
1 // Save modified state the control would like restored on the postback.
2 // Return null if there is no state to save.
3
4 /**//// <devdoc>
5 /// <para>
6 /// Saves view state for use with a later <see cref='System.Web.UI.Control.LoadViewState'/>
7 /// request.
8 /// </para>
9 /// </devdoc>
10 protected virtual object SaveViewState() {
11 // Save values cached out of view state
12 if (flags[visibleDirty]) {
13 ViewState["Visible"] = !flags[invisible];
14 }
15 if (_viewState != null)
16 return _viewState.SaveViewState();
17
18 return null;
19 }
2 // Return null if there is no state to save.
3
4 /**//// <devdoc>
5 /// <para>
6 /// Saves view state for use with a later <see cref='System.Web.UI.Control.LoadViewState'/>
7 /// request.
8 /// </para>
9 /// </devdoc>
10 protected virtual object SaveViewState() {
11 // Save values cached out of view state
12 if (flags[visibleDirty]) {
13 ViewState["Visible"] = !flags[invisible];
14 }
15 if (_viewState != null)
16 return _viewState.SaveViewState();
17
18 return null;
19 }
第12行代码判断控件是否可见,第15行代码判断_viewState是否为null,如果在某一个控件中,所有的操作中都没有使用控件本身的ViewState属性,那么_viewState就是null,这种情况一般是不存在的。如果_viewState不为null,由于_viewState对象的类型是StateBag,则调用StataBag.SaveViewState。
1private IDictionary bag;
2 /*
3 * Constructs an StateBag
4 */
5
6 /// <devdoc>
7 /// <para>Initializes a new instance of the <see cref='System.Web.UI.StateBag'/> class.</para>
8 /// </devdoc>
9 public StateBag() : this(false) {
10 }
11
12 /*
13 * Constructs an StateBag
14 */
15
16 /// <devdoc>
17 /// <para>Initializes a new instance of the <see cref='System.Web.UI.StateBag'/> class that allows stored state
18 /// values to be case-insensitive.</para>
19 /// </devdoc>
20 public StateBag(bool ignoreCase) {
21 marked = false;
22 this.ignoreCase = ignoreCase;
23 bag = CreateBag();
24 }
25 private IDictionary CreateBag() {
26 return new HybridDictionary(ignoreCase);
27 }
2 /*
3 * Constructs an StateBag
4 */
5
6 /// <devdoc>
7 /// <para>Initializes a new instance of the <see cref='System.Web.UI.StateBag'/> class.</para>
8 /// </devdoc>
9 public StateBag() : this(false) {
10 }
11
12 /*
13 * Constructs an StateBag
14 */
15
16 /// <devdoc>
17 /// <para>Initializes a new instance of the <see cref='System.Web.UI.StateBag'/> class that allows stored state
18 /// values to be case-insensitive.</para>
19 /// </devdoc>
20 public StateBag(bool ignoreCase) {
21 marked = false;
22 this.ignoreCase = ignoreCase;
23 bag = CreateBag();
24 }
25 private IDictionary CreateBag() {
26 return new HybridDictionary(ignoreCase);
27 }
1 /*
2 * Return object containing state that has been modified since "mark".
3 * Returns null if there is no modified state.
4 */
5
6 /// <devdoc>
7 /// <para>Returns an object that contains all state changes for items stored in the
8 /// <see langword='StateBag'/> object.</para>
9 /// </devdoc>
10 internal object SaveViewState() {
11 ArrayList data = null;
12
13 //
14
15 if (bag.Count != 0) {
16 IDictionaryEnumerator e = bag.GetEnumerator();
17 while (e.MoveNext()) {
18 StateItem item = (StateItem)(e.Value);
19 if (item.IsDirty) {
20 if (data == null) {
21 data = new ArrayList();
22 }
23#if OBJECTSTATEFORMATTER
24 data.Add(new IndexedString((string)e.Key));
25#else
26 data.Add(e.Key);
27#endif
28 data.Add(item.Value);
29 }
30 }
31 }
32
33 return data;
34 }
2 * Return object containing state that has been modified since "mark".
3 * Returns null if there is no modified state.
4 */
5
6 /// <devdoc>
7 /// <para>Returns an object that contains all state changes for items stored in the
8 /// <see langword='StateBag'/> object.</para>
9 /// </devdoc>
10 internal object SaveViewState() {
11 ArrayList data = null;
12
13 //
14
15 if (bag.Count != 0) {
16 IDictionaryEnumerator e = bag.GetEnumerator();
17 while (e.MoveNext()) {
18 StateItem item = (StateItem)(e.Value);
19 if (item.IsDirty) {
20 if (data == null) {
21 data = new ArrayList();
22 }
23#if OBJECTSTATEFORMATTER
24 data.Add(new IndexedString((string)e.Key));
25#else
26 data.Add(e.Key);
27#endif
28 data.Add(item.Value);
29 }
30 }
31 }
32
33 return data;
34 }
通过上边的代码可以看出,SateBag.SaveViewState方法中的bag是HybridDictionary类型的实例,HybridDictionary类型实现了 IEnumerable接口,我们可以使用循环算法,依次取出其中的元素,如果有如何条件的元素,他们将会被保存在data对象里边,然后返回data对象。至此,控件的ViewState保存完毕。
这里边有一个关键的地方,那就是StateBag的IsDirty属性。
IsDirty属性和我们平时使用的ViewState有什么关系呢?默认情况下,IsDirty属性为false,它是怎么变成true的呢?要理解这些,请看下边的代码:
void Page_Load(Object sender, EventArgs e)
{
this.Literal1.Text = "Hello World!";
}
Literal.Text源码{
this.Literal1.Text = "Hello World!";
}
1 /**//// <devdoc>
2 /// [To be supplied.]
3 /// </devdoc>
4 [
5 Localizable(true),
6 Bindable(true),
7 WebCategory("Appearance"),
8 DefaultValue(""),
9 WebSysDescription(SR.Literal_Text),
10 ]
11 public string Text {
12 get {
13 string s = (string)ViewState["Text"];
14 return (s != null) ? s : String.Empty;
15 }
16 set {
17 ViewState["Text"] = value;
18 }
19 }
2 /// [To be supplied.]
3 /// </devdoc>
4 [
5 Localizable(true),
6 Bindable(true),
7 WebCategory("Appearance"),
8 DefaultValue(""),
9 WebSysDescription(SR.Literal_Text),
10 ]
11 public string Text {
12 get {
13 string s = (string)ViewState["Text"];
14 return (s != null) ? s : String.Empty;
15 }
16 set {
17 ViewState["Text"] = value;
18 }
19 }
从上边两段代码可以看出,我们用上边的代码对Literal控件的Text属性赋值,相当于ViewState["Text"] = "Hello World!";,我们前边已经分析过,ViewState属性是StateBag类型的对象,StateBag的this属性源码为:
1Code
2 /**//// <devdoc>
3 /// <para> Indicates the value of an item stored in the
4 /// <see langword='StateBag'/>
5 /// object. Setting this property with a key not already stored in the StateBag will
6 /// add an item to the bag. If you set this property to <see langword='null'/> before
7 /// the TrackState method is called on an item will remove it from the bag. Otherwise,
8 /// when you set this property to <see langword='null'/>
9 /// the key will be saved to allow tracking of the item's state.</para>
10 /// </devdoc>
11 public object this[string key] {
12 get {
13 if (String.IsNullOrEmpty(key))
14 throw ExceptionUtil.ParameterNullOrEmpty("key");
15
16 StateItem item = bag[key] as StateItem;
17 if (item != null)
18 return item.Value;
19 return null;
20 }
21 set {
22 Add(key,value);
23 }
24 }
StateBag.Add方法源码:2 /**//// <devdoc>
3 /// <para> Indicates the value of an item stored in the
4 /// <see langword='StateBag'/>
5 /// object. Setting this property with a key not already stored in the StateBag will
6 /// add an item to the bag. If you set this property to <see langword='null'/> before
7 /// the TrackState method is called on an item will remove it from the bag. Otherwise,
8 /// when you set this property to <see langword='null'/>
9 /// the key will be saved to allow tracking of the item's state.</para>
10 /// </devdoc>
11 public object this[string key] {
12 get {
13 if (String.IsNullOrEmpty(key))
14 throw ExceptionUtil.ParameterNullOrEmpty("key");
15
16 StateItem item = bag[key] as StateItem;
17 if (item != null)
18 return item.Value;
19 return null;
20 }
21 set {
22 Add(key,value);
23 }
24 }
1Code
2 /**//*
3 * Add a new StateItem or update an existing StateItem in the bag.
4 */
5
6 /**//// <devdoc>
7 /// <para>[To be supplied.]</para>
8 /// </devdoc>
9 public StateItem Add(string key,object value) {
10
11 if (String.IsNullOrEmpty(key))
12 throw ExceptionUtil.ParameterNullOrEmpty("key");
13
14 StateItem item = bag[key] as StateItem;
15
16 if (item == null) {
17 if (value != null || marked) {
18 item = new StateItem(value);
19 bag.Add(key,item);
20 }
21 }
22 else {
23 if (value == null && !marked) {
24 bag.Remove(key);
25 }
26 else {
27 item.Value = value;
28 }
29 }
30 if (item != null && marked) {
31 item.IsDirty = true;
32 }
33 return item;
34 }
2 /**//*
3 * Add a new StateItem or update an existing StateItem in the bag.
4 */
5
6 /**//// <devdoc>
7 /// <para>[To be supplied.]</para>
8 /// </devdoc>
9 public StateItem Add(string key,object value) {
10
11 if (String.IsNullOrEmpty(key))
12 throw ExceptionUtil.ParameterNullOrEmpty("key");
13
14 StateItem item = bag[key] as StateItem;
15
16 if (item == null) {
17 if (value != null || marked) {
18 item = new StateItem(value);
19 bag.Add(key,item);
20 }
21 }
22 else {
23 if (value == null && !marked) {
24 bag.Remove(key);
25 }
26 else {
27 item.Value = value;
28 }
29 }
30 if (item != null && marked) {
31 item.IsDirty = true;
32 }
33 return item;
34 }
上边两段代码都不复杂,有一个地方需要我们注意,就是StateBag的Add方法,我们可以看到if(item != null && marked)条件成立的话,这个item的IsDirty属性就被赋值为true,item不为null这个好理解,marked是怎么成为true的呢,默认情况下,这个字段的值是false。
要理解这点,就要从页面的生命周期开始说起,页面在"Begin Init"阶段,调用Control.InitRecursive(null)方法,在这个方法的内部,调用Control.TrackViewState()方法,下边列出Control.TrackViewState()方法的源码:
/// <devdoc>
/// <para>Turns on tracking of view state changes to the control
/// so that they can be stored in the <see langword='StateBag'/>
/// object.</para>
/// </devdoc>
protected virtual void TrackViewState() {
if (_viewState != null)
_viewState.TrackViewState();
flags.Set(marked);
}
StateBag.TrackViewState()源码:/// <para>Turns on tracking of view state changes to the control
/// so that they can be stored in the <see langword='StateBag'/>
/// object.</para>
/// </devdoc>
protected virtual void TrackViewState() {
if (_viewState != null)
_viewState.TrackViewState();
flags.Set(marked);
}
从上边两段代码,可以看出,我们前边提到的marked字段,在这个方法中被赋值为true了。