1. ViewState介绍:
(1) ViewState 不是用来恢复回发的控件的值。这个是通过匹配 form 中该控件的变量名而自动完成的。这个只对 Load 事件加载之前创建的控件有效。(2) ViewState 不会自动创建任何动态创建的控件。
(3) 不是用来保存用户信息的。仅仅保存本页的控件状态,而不能在页面之间传递。当这个页面关闭之后,ViewState也会随之消失。
(4) ViewState 用来跟踪和保存控件的状态信息。否则这些信息可能会丢失,原因可能是这些值不随着 form 回发,或者根本就不在 page 的 html 中。(5)ViewState 中保存着代码中改变的控件属性,通过代码绑定到控件的任何数据,以及由用户操作触发,回发的任何更改。ViewState 还提供了一个状态包(StateBag), 这是一个特殊的集合或字典(collection or dictionary), 可以用来保存,通过一个 key 来恢复任意的对象或者值。
(5)ViewState 的格式 : 保存在表单中的 __VIEWSTATE 隐藏字段。是 Base64 编码过的,而不是加密!但要加密也是可以的(设置 enableViewStateMac 来使用 machine key 进行 hash)加密:设置 machineKey 验证, 但这必须在机器级别设置,需要更多的资源,所以不推荐。
(6)ViewState机制是asp.net中对同一个Page的多次请求(PostBack)之间维持Page及控件状态的一种机制。在WebForm中每次请求完,Page对象都会被释放,对同一个Page的多次请求之间的状态信息, WebForm中,每次请求都会存在客户端和服务器之间的一个交互。如果请求完成之后将一些信息传回到客户端,下次请求的时候客户端再将这些状态信息提交给服务器,服务器端对这些信息使用和处理,再将这些信息传回给客户端。这样是不是就可以对同一个Page的多次请求(PostBack)之间维持状态了。对这就是ViewState的基本工作模式。ViewState的设计目的主要就是为了将必要的信息持久化在页面中。这样通过ViewState在页面回传的过程中保存状态值,使原本没有“记忆”的Http协议变得有“记忆”起来。
(7)ViewState工作分为客户端和服务器端,
客户端:<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE"
value="/wEPDwULLTE0MTAzNDUwNThkZKr77J2uy7fatyBou8PocG80X4Jt" />
这个就是ViewState在客户端的保存形式,它保存在一个ID为__VIEWSTATE的Hidden中,它的Value是使用Base64编码后的字符串。这个字符串实际上是一个对象(Pair类型)序列化之后的结果。这个对象保存了整个页面的控件树的ViewState。可以使用一些工具将这个字符串进行解码查看其内容,比如ViewStateDecoder,ViewStateAnalyzer。
服务器端:在服务器端和ViewState机制密切相关的有三个类Page,Control,StateBag
Page继承自Control,Control和StateBag是聚合关系,在Control中有一个StateBag的实例ViewState。这三个类互相协作完成ViewState机制的大概过程如下。Page对客户端请求进行处理,在处理的过程中先是将客户端提交的_VIEWSTATE反序列化为对象,调用Control的相关方法给所有的控件装载数据,这些数据是上次请求结束后控件的状态数据。在之后的一些事件中这些状态数据可能被修改。在请求结束之前调用Control的相关方法得到所有控件的被修改过的状态数据,之后Page将其进行序列化,并返回给客户端。在Control中又具体调用StateBag类的方法完成状态数据的加载和保存。
1) InitRecursive
在Page的生命周期中有3处与ViewState相关,在初始化阶段调用Control. InitRecursive,它递归对所有的控件进行初始化,其中调用了Control.TrackViewState。TrackViewState中打开跟踪ViewState开关。
2) LoadAllState
在初始化完成之后会调用Page.LoadAllState,LoadAllState只有在PostBack的时候才会执行,它的主要功能是将从页面传递来的__VIEWSTATE的值反序列化为Pair类型的对象,然后将这个对象中存储的ViewState的值加载到Page及所有控件中。实际上LoadAllState加载了ControlState(控件状态)及ViewState(视图状态),本文主要是讨论ViewState,对ControlState部分的处理不进行描述。
LoadAllState中主要有两步:Page.LoadPageStateFromPersistenceMedium和Control.LoadViewStateRecursive。
在LoadPageStateFromPersistenceMedium中发生了如下的调用层次
Page.LoadPageStateFromPersistenceMedium
HiddenFieldPageStatePersister.Load
ObjectStateFormatter.Deserialize
以上完成的功能是将客户端提交的_VIEWSTATE反序列化为一个类型为Pair的对象pair。
Control.LoadViewStateRecursive中将递归加载控件的ViewState,具体在下面进行讲解。
3) SaveAllState
SaveAllState它的操作和LoadAllState相反。SaveAllState中主要有两步Control.SaveViewStateRecursive及Page SavePageStateToPersistenceMedium。
Control.SaveViewStateRecursive中将所有控件的ViewState属性递归加载到一个Pair对象中,具体实现细节在下面讲解。
Page SavePageStateToPersistenceMedium中发生如下的调用关系。
Page SavePageStateToPersistenceMedium
HiddenFieldPageStatePersister.Save
ObjectStateFormatter.Serialize
将Control.SaveViewStateRecursive生成的对象序列化为一个字符串,并赋值给Page.ClientState属性。
在Render阶段发生如下的调用关系:
HtmlForm.RenderChildren
Page.BeginFormRender
Page.RenderViewStateFields
最终将ClientState属性中的值写入到HTML页面的_VIEWSTATE中。
在Control.InitRecursive中打开跟踪开关,打算对ViewState的值进行跟踪,在Page.LoadAllState中将客户端提交的__VIEWSTATE的值装载到各个控件的ViewState中,在Page.SaveAllState中将发生变化的ViewState序列化为一个字符串,在Render阶段发送回客户端。
Pair类及ViewState的存储
Page及所有控件的ViewState、ControlState都是存储在Pair类的实例中,了解Pair类及ViewState如何存储在Pair类中很重要。Pair定义的System.Web.UI中具体定义如下:
public sealed class Pair
{
public object First;
public object Second;
public Pair();
public Pair(object x, object y);
}
可以看出Pair类是用作存储两个相关对象的基本结构。Pair是一个工具类,使用它很容易形成一个树的数据结构。将__VIEWSTATE反序列化后就是一个Pair对象。这个对象即保存了控件之间的父子关系,也保存了ViewState信息。ViewState中的Key/Value对被转换为一个ArrayList对象保存在Pair对象中。下面我们来看看它是如何存储的。
Control中的处理
Page类继承自Control类,在实现ViewState机制的时候Page类中主要是涉及序列化和反序列化的工作,ViewState的保存、装载等功能都在Control类中实现由Page类来继承,控件也都继承自Control类,下面对Control类中和ViewState机制相关的属性、方法进行介绍。1) ViewState属性ViewState是Page及控件的一个属性,这个属性并不是在Page或控件中定义,它定义在System.Web.UI.Control类中。它的声明如下:protected virtual StateBag ViewState{get}
由于所有服务器端的控件,用户自定义控件,及Page类都是继承自Control类,所以它们都会都具有一个protected的ViewState属性。这个ViewState属性在ViewState机制中很重要,它用来存储控件需要记忆的一些数据。这些数据主要有两类,一类是需要记忆的控件的属性。另外一类是我们想利用ViewState机制来记住的一些数据,比如我们在Web窗体中写如下的代码: protected void Page_Load(object sender, EventArgs e)
{
if (!this.IsPostBack)
{
this.ViewState["Test"] = 0;
}
else
{
this.ViewState["Test"]=int.Parse(this.ViewState["Test"].ToString()) + 1;
}
}因为我们创建的Web窗体都是继承自Page类,所以在Web窗体中能否访问Page类的protected的ViewState属性。我们可以通过ViewState属性利用ViewState机制,让它帮助我们记住一些信息。ViewState属性的主要作用还是用来记住一些控件的属性值。ViewState属性和控件的其他属性有什么样的联系,才能够利用ViewState属性来记住他们呢?下面用Button类的Text属性举例来说明,Text属性和ViewState属性是什么关系。Button.Text的定义如下:
public string Text
{
get
{
string str = (string) this.ViewState["Text"];
if (str != null)
{
return str;
}
return string.Empty;
}
set
{
this.ViewState["Text"] = value;
}
}
通过上面的代码我们可以看出Button.Text属性的值实际上和ViewState中Key为“Text”的Value是关联的。当然Button类的其他属性也是类似的方式,只是对应ViewState中Key不同而已。这样只要ViewState的值能够被记忆,那么Button类的属性也就能够被记忆住了。记忆ViewState的方法就是在Page. SaveAllState中将所有控件的ViewState属性生成一个对象(这是一个特殊的对象,它是一个树状态的存储了所有控件的ViewState属性),然后将这个对象序列化为字符串,发送到客户端。在下次请求的时候,将这个字符串发送给服务器,在Page.LoadAllState中将这个字符串反序列化为一个对象,将这个对象中存储的各个控件的ViewState属性,加载给各个控件的ViewState属性。当我们访问控件的属性的时候就实现了对控件属性的记忆,之前设置的控件属性没有丢失。2) LoadViewStateByID属性LoadViewStateByID的声明如下:
protected bool LoadViewStateByID{get}
LoadViewStateByID获取一个值,该值指示控件是否通过ID方式加载其视图状态。默认情况下是通过索引方式加载视图状态,索引方式是依赖子控件在父控件的Controls集合中位置。ID方式会根据ID查找控件,效率比较低些,但是有些情形必须使用这种方式比如延迟创建子控件时。
3) EnableViewState属性EnableViewState是一个bool类型的属性,来决定当前控件的ViewState机制是否可用。其声明如下:public virtual bool EnableViewState{get,set}
4) LoadViewStateRecursiveLoadViewStateRecursive的声明如下:internalvoid LoadViewStateRecursive(object savedState)Page.LoadAllState中调用Control.LoadViewStateRecursive,传入的参数savedState是一个Pair类型的对象,Pair.Fisrt中保存当前控件的ViewState,Pair.Second中保存子控件的ViewState。对于EnableViewState为true时,先通过调用Control.LoadViewState(savedState.First)装载当前控件的ViewState。之后根据LoadViewStateByID属性,装载子控件的ViewState。如果LoadViewStateByID属性为true调用Control.LoadChildViewStateByID(savedState.Second),否则调用Control.LoadChildViewStateByIndex(savedState.Second)。savedState.Second是一个ArrayList类型的对象。Control.LoadViewState的声明如下:
protected virtual void LoadViewState(object savedState)在Control.LoadViewState中并没有自己实现装载当前控件的ViewState,而是通过ViewState.LoadViewState(savedState)来实现,下面会介绍StateBag的LoadViewState方法。LoadChildViewStateByID的声明如下:internal void LoadChildViewStateByID(ArrayList childState)参数childState是ArrayList类型,childState中存储了当前控件的所有子控件的信息。它的格式是首先是一个控件的ID,其后是这个控件的Pair对象。对childState进行循环,循环中取得控件的ID,根据ID找到控件调用这个控件的LoadViewStateRecursive方法。示意代码如下:for (int i = 0; i < count; i += 2)
{
string id = (string) childState[i];
object savedState = childState[i + 1];
Control control = this.FindControl(id);
control.LoadViewStateRecursive(savedState);
}
LoadChildViewStateByIndex的声明如下:internal void LoadChildViewStateByIndex(ArrayList childState)
childState的存储格式是首先是一个控件的索引,其后是这个控件的Pair对象。根据索引访问访问这个控件,调用其LoadViewStateRecursive方法,示例代码如下:
for (int i = 0; i < num2; i += 2)
{
int num4 = (int) childState[i];
object savedState = childState[i + 1];
controls[num4].LoadViewStateRecursive(savedState);
}
5) LoadViewState在LoadViewState中直接调用ViewState的LoadViewState方法进行ViewState的装载,示意代码如下:
this.ViewState.LoadViewState(savedState);
6) SaveViewStateRecursiveSaveViewStateRecursive的声明如下:internal object SaveViewStateRecursive()
对于EnableViewState为true时,先调用Control.SaveViewState返回一个包含当前控件的ViewState信息的ArrayList类型对象x。之后对子控件进行递归处理获得一个ArrayList类型的对象z。它的格式是ID(String),savedState(Pair)或者Index(int)savedState(Pair)。最后创建一个Pair对象Pair(x, z)。示意代码如下:
object x = this.SaveViewState();
ArrayList z = null;
ControlCollection controls = this._occasionalFields.Controls;
int count = controls.Count;
bool loadViewStateByID = this.LoadViewStateByID;
for (int i = 0; i < count; i++)
{
Control control = controls[i];
object obj4 = control.SaveViewStateRecursive();
if (loadViewStateByID)
z.Add(control.ID);
else
z.Add(i);
z.Add(obj4);
}
return new Pair(x, z);
7) SaveViewState在SaveViewState中直接调用ViewState的SaveViewState方法进行ViewState的保存,示意代码如下:
return this.ViewState.SaveViewState();
8) TrackViewState在Control.InitRecursive中会调用Control.TrackViewState,因为Control.InitRecursive是被递归调用的,所以每个控件的TrackViewState都会在初始化阶段被调用到。Control.TrackViewState中之间调用ViewState的TrackViewState方法,示意代码如下:
this.ViewState.TrackViewState();
StateBag类
ViewState是控件的一个属性,用来使用控件具有记忆功能。在前边的讲述中,我们可以看到控件的一些属性通过使用ViewState能够恢复原来的值,保存本次的值,在Control类中很多方法的实现也是直接调用了ViewState的方法。ViewState的类型是StateBag,下面我们就了解一下在StateBag中是如何实现这些功能的。StateBag定义在System.Web.UI中声明如下:
public sealed class StateBag : IStateManager, IDictionary, ICollection, IEnumerable
StateBag类可以理解为是一个具有状态管理功能的字典,因为它实现了IStateManager, IDictionary 这两个接口。StateBag类可以象字典那样保存Key/Value对,其中Key是字符串而Value是对象。下面是一个使用StateBag的例子。
protected void Button2_Click(object sender, EventArgs e)
{
StateBag TestSB = new StateBag();
TestSB["b"] = "bbbbb";
TextBox1.Text = TestSB["b"].ToString();
}
在上面的例子中使用StateBag保存一个Key为“b”,其值为“bbbbb”的Key/Value对。ViewState属性也是StateBag的一个实例,当然也就可以象上面那样使用。在ViewState中保存了很多的Key/Value对(键值对),这些Key/Value对用来保存控件的属性,这些Key/Value对是有ASP.Net来维护的。当然我们也可以增加一些自己的Key/Value对,来保存一些信息。
StateBag还实现System.Web.UI.IStateManager接口,这样它具有状态管理功能。下面对StateBag如何提供状态管理功能进行说明。
1) StateItem类
StateBag中保存Key/Value对,Key是String类型,Value是Object类型。但是在StateBag内部保存Value不是Object类型,而是将Object类型转换为StateItem类型然后保存,从StateBag中取出的时候再将StateItem类型转换为Object类型,也就是说StateBag中的Key/Value对实际上是String/StateItem类型。转换过程是在StateBag内部实现客户感觉不到。StateItem的声明如下:
public sealed class StateItem
{
internal StateItem(object initialValue);
public bool IsDirty { get; set; }
public object Value { get; set; }
}
通过上面的代码我们可以看出实际上多了一个IsDirty属性,来标记当前的Value是否已经被修改过。
2) Add
Add方法是将传入的Key,Value保存到字典中,并处理IsDirty属性。在StateBag.LoadViewState方法中会调用Add方法。其示意代码如下:
public StateItem Add(string key, object value)
{
StateItem item = this.bag[key] as StateItem;
if (item == null)
{
if ((value != null) || this.marked)
{
item = new StateItem(value);
this.bag.Add(key, item);
}
}
else
{
item.Value = value;
}
if ((item != null) && this.marked)
{
item.IsDirty = true;
}
return item;
}
虽然函数的名称是Add,其实也包括了更新。如果当前的项在字典中不存在则新增,否则更新。新增时新建一个StateItem类型的对象item,将Key和item增加到字典中。如果Item不为null,并且跟踪状态标记为true,则item的IsDirty为true。什么情况下会调用Add方法呢?主要有两种情形一种是StateBag.LoadViewState,在下面会具体介绍到。还有一种情况就是对控件的属性赋值的时候,比如Button.Text=”button”,此时会调用Text属性的Set,在Set中执行的代码this.ViewState["Text"] = value,这个代码实际上执行this.ViewState.Add("Text",value)。
3) LoadViewState
还原以前保存的ViewState。将传入的ArrayList对象加载到字典中。示意代码如下:
internal void LoadViewState(object state){ ArrayList list = (ArrayList) state; for (int i = 0; i < list.Count; i += 2) { string key = ((IndexedString) list[i]).Value; object obj2 = list[i + 1]; this.Add(key, obj2); }}
我们知道在初始化阶段StateBag.TrackViewState都已经被调用过了,也就是说marked为true了,这样在StateBag.LoadViewState中调用Add方法第一次加载完ViewState的数据后,所有的StateItem的IsDirty属性都是true。
4) SaveViewState
将字典中已经修改过的Key/Value存放在一个ArrayList对象中返回。
internal object SaveViewState(){ ArrayList list = null; IDictionaryEnumerator enumerator = this.bag.GetEnumerator(); while (enumerator.MoveNext()) { StateItem item = (StateItem) enumerator.Value; if (item.IsDirty) { list.Add(new IndexedString((string) enumerator.Key)); list.Add(item.Value); } } } return list;}
在SaveViewState中只会将字典中item.IsDirty=true的项目返回,哪些项的IsDirty=true呢?通过上面对Add方法及LoadViewState的分析我们知道,当我们第一次设置了控件的某个属性后会调用Add方法,这个属性对应的StateItem的属性IsDirty会被设置为true,在SaveViewState时会保存这个item。在下次请求时在LoadViewState中也会将IsDirty设置为true,在SaveViewState是会保存这个item。总之只要控件属性的被修改过和默认值不一致都会一直被保存,即使这个属性的值仅仅被修改过一次,之后保存不不变,也会在多次PostBack之间保存起来。
5) TrackViewState
在TrackViewState方法中,设置跟踪标记为True,其目的就是开始状态跟踪了。示意代码如下:
internal void TrackViewState()
{
this.marked = true;
}
3 ViewState与ControlState
ControlState是一个自定义的状态保持机制,也就是说保持状态的机制需要开发人员自 己去完成,而不像ViewState,它有自己默认的状态保持机制。既然已经有了ViewState 为什么还需要ControlState呢?因为ViewState是可以被禁用的,而ControlState却不能被禁用,对于有些必须保存的信息,就可以使用ControlState。ControlState的实现思路基本上与ViewState类似,ControlState需要保存的信息也被序列化后保存在__VIEWSTATE中。使用ControlState,需要在OnInit 方法中调用 RegisterRequiresControlState向页面注册,而且要重写SaveAdapterControlState,LoadAdapterControlState这两个方法自己,实现要保存什么,怎样保存。
ViewState Machine Hash Disabled
machine.config or web.config: <pages enableViewStateMac='false' />
page level directive: <%@Page enableViewStateMac='false' %>
page level script code: Page.EnableViewStateMac = false;
ViewState Encryption is Enabled
machine.config: <machineKey validation='3DES' validationKey='*' />
where the validationKey must be the same across a web-farm setup
also requires the enableViewStateMac property setting to be true
在 rendering 之前,ViewState 在 Page.SavePageStateToPersistenceMedium 方法中被保存,
回发时,在 Page.LoadPageStateFromPersistanceMedium 方法中被恢复。
这两个方法都可以轻易的被重写,从而实现保存 ViewState 到 Session 中。这适合于带宽小的场合,
如移动设备默认是采用 Session.代码如下:
ViewState Saved in Session State
protected override object LoadPageStateFromPersistenceMedium()
{
return Session["ViewState"];
}
protected override void SavePageStateToPersistenceMedium(object viewState)
{
Session["ViewState"] = viewState;
// Bug requires Hidden Form Field __VIEWSTATE
RegisterHiddenField("__VIEWSTATE", "");
}
如果要把 ViewState 通过数据库或其他持久化设备来维持,则需要采用特定的 LosFormatter 类来序列化,反序列化。(serialize, deserialize)
ViewState Saved in Custom Store
protected override object LoadPageStateFromPersistenceMedium()
{
LosFormatter format = new LosFormatter();
return format.Deserialize(YourDataStore["ViewState"]);
}
protected override void SavePageStateToPersistenceMedium(object viewState)
{
LosFormatter format = new LosFormatter();
StringWriter writer = new StringWriter();
format.Serialize(writer, viewState);
YourDataStore["ViewState"] = writer.ToString();
}
最后,我们来看一下 ViewState 的内部格式到底是什么。
每个控件的 ViewState 保存在一个三元组中(Triplet, System.Web.UI.Triplet).
其 First 对象是:一个 Pair(System.Web.UI.Pair)或Array or Pairs, of ArrayLists of related name-values.
Second 对象:
该控件在控件树中的索引的 ArrayList
Third 对象:
子控件的类似的三元组的 ArrayList
ViewState Decode/Parse Example
编码后的 ViewState:
dDwxMjM0NTY3ODkwO3Q8cDxsPHBycEE7cHJwQjtwcnBDOz47bDx2YWxBO3ZhbEI7dmFsQzs+PjtsPGk8
MD47aTwyPjtpPDM+O2k8NT47PjtsPHQ8cDxsPHBycEE7cHJwQjs+O2w8dmFsQTt2YWxCOz4+Ozs+O3Q8
cDxsPHBycEE7cHJwQjs+O2w8dmFsQTt2YWxCOz4+Ozs+O3Q8cDxsPHBycEE7cHJwQjs+O2w8dmFsQTt2
YWxCOz4+Ozs+O3Q8cDxsPHBycEE7cHJwQjs+O2w8dmFsQTt2YWxCOz4+Ozs+Oz4+Oz4=
解码后的 ViewState:
t<1234567890;t<p<l<prpA;prpB;prpC;>;l<valA;valB;valC;>>;
l<i<0>;i<2>;i<3>;i<5>;>;l<
t<p<l<prpA;prpB;>;l<valA;valB;>>;;>;
t<p<l<prpA;prpB;>;l<valA;valB;>>;;>;
t<p<l<prpA;prpB;>;l<valA;valB;>>;;>;
t<p<l<prpA;prpB;>;l<valA;valB;>>;;>;>>;>
解析后的 ViewState:
t<1234567890; 页面级别的三元组是特例
t<p<l<prpA;prpB;prpC;>; Triplet-First:Pair-First:ArrayList
l<valA;valB;valC;> Pair-Second:ArrayList
>;
l<i<0>; Triplet-Second:ArrayList:Indices
i<2>; of the
i<3>; Children
i<5>; Controls
>;
l<t<p<l<prpA;prpB;>; Triplet-Third:ArrayList:Triplets
l<valA;valB;> of the
>; Children
; Controls
>;
t<p<l<prpA;prpB;>; Each Sub-Triplet follows same Pattern
l<valA;valB;>
>;
; More Levels Possible if sub-Children
>;
t<p<l<prpA;prpB;>; Each Sub-Triplet follows same Pattern
l<valA;valB;>
>;
; More Levels Possible if sub-Children
>;
t<p<l<prpA;prpB;>; Each Sub-Triplet follows same Pattern
l<valA;valB;>
>;
; More Levels Possible if sub-Children
>;
>
>; Closing of Special Page-Level Triplet
>
using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Web;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using System.IO;
using System.Text;
namespace MyPlayground
{
/// <summary>
/// ShowViewState 的摘要说明。
/// </summary>
public class ShowViewState : System.Web.UI.Page
{
private void Page_Load(object sender, System.EventArgs e)
{
//Trace.Warn("分类名称", "^_^,这是警告!自动用红色字显示");
//Trace.Write("这是普通的消息写入!");
}
protected override void SavePageStateToPersistenceMedium(object viewState)
{
// 调用基类的方法以便不影响正常的处理
base.SavePageStateToPersistenceMedium(viewState);
// 读取 ViewState 并写到页面
LosFormatter format = new LosFormatter();
StringWriter writer = new StringWriter();
format.Serialize(writer, viewState);
string vsRaw = writer.ToString();
Response.Write("ViewState Raw: " + Server.HtmlEncode(vsRaw) + "<hr>");
// 解码 ViewState 并写到页面
byte[] buffer = Convert.FromBase64String(vsRaw);
string vsText = Encoding.ASCII.GetString(buffer);
Response.Write("ViewState Text: " + Server.HtmlEncode(vsText) + "<hr>");
// 解析 ViewState -- 打开页面跟踪(Tracing)
ParseViewState(viewState, 0);
}
private void ParseViewState(object vs, int level)
{
if (vs == null)
{
Trace.Warn(level.ToString(), Spaces(level) + "null");
}
else if (vs.GetType() == typeof(System.Web.UI.Triplet))
{
Trace.Warn(level.ToString(), Spaces(level) + "Triplet");
ParseViewState((Triplet) vs, level);
}
else if (vs.GetType() == typeof(System.Web.UI.Pair))
{
Trace.Warn(level.ToString(), Spaces(level) + "Pair");
ParseViewState((Pair) vs, level);
}
else if (vs.GetType() == typeof(System.Collections.ArrayList)) {
Trace.Warn(level.ToString(), Spaces(level) + "ArrayList");
ParseViewState((IEnumerable) vs, level);
}
else if (vs.GetType().IsArray)
{
Trace.Warn(level.ToString(), Spaces(level) + "Array");
ParseViewState((IEnumerable) vs, level);
}
else if (vs.GetType() == typeof(System.String))
{
Trace.Warn(level.ToString(), Spaces(level) + "'" + vs.ToString() + "'");
}
else if (vs.GetType().IsPrimitive)
{
Trace.Warn(level.ToString(), Spaces(level) + vs.ToString());
}
else
{
Trace.Warn(level.ToString(), Spaces(level) + vs.GetType().ToString());
}
}
private void ParseViewState(Triplet vs, int level)
{
ParseViewState(vs.First, level + 1);
ParseViewState(vs.Second, level + 1);
ParseViewState(vs.Third, level + 1);
}
private void ParseViewState(Pair vs, int level)
{
ParseViewState(vs.First, level + 1);
ParseViewState(vs.Second, level + 1);
}
private void ParseViewState(IEnumerable vs, int level)
{
foreach (object item in vs)
{
ParseViewState(item, level + 1);
}
}
// 得到指定数目的空白
private string Spaces(int count)
{
string spaces = ""
for (int index = 0; index < count; index++)
{
spaces += " "
}
return spaces;
}
}
}