Control.SaveViewState是如何保存视图信息的
我们平时所见到的web服务器控件,一般而言,其直接的基类,不是WebControl就是Control,我们今天说说Control的SaveViewState方法,这个方法的源码如下:

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

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

第12行代码判断控件是否可见,第15行代码判断_viewState是否为null,如果在某一个控件中,所有的操作中都没有使用控件本身的ViewState属性,那么_viewState就是null,这种情况一般是不存在的。如果_viewState不为null,由于_viewState对象的类型是StateBag,则调用StataBag.SaveViewState。
1
private 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

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

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

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

通过上边的代码可以看出,SateBag.SaveViewState方法中的bag是HybridDictionary类型的实例,HybridDictionary类型实现了 IEnumerable接口,我们可以使用循环算法,依次取出其中的元素,如果有如何条件的元素,他们将会被保存在data对象里边,然后返回data对象。至此,控件的ViewState保存完毕。
这里边有一个关键的地方,那就是StateBag的IsDirty属性。
IsDirty属性和我们平时使用的ViewState有什么关系呢?默认情况下,IsDirty属性为false,它是怎么变成true的呢?要理解这些,请看下边的代码:




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

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

从上边两段代码可以看出,我们用上边的代码对Literal控件的Text属性赋值,相当于ViewState["Text"] = "Hello World!";,我们前边已经分析过,ViewState属性是StateBag类型的对象,StateBag的this属性源码为:
1
Code
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

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

1
Code
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

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

上边两段代码都不复杂,有一个地方需要我们注意,就是StateBag的Add方法,我们可以看到if(item != null && marked)条件成立的话,这个item的IsDirty属性就被赋值为true,item不为null这个好理解,marked是怎么成为true的呢,默认情况下,这个字段的值是false。
要理解这点,就要从页面的生命周期开始说起,页面在"Begin Init"阶段,调用Control.InitRecursive(null)方法,在这个方法的内部,调用Control.TrackViewState()方法,下边列出Control.TrackViewState()方法的源码:











从上边两段代码,可以看出,我们前边提到的marked字段,在这个方法中被赋值为true了。