在ASP.NET自定义控件开发中,如果需要自己实现控件的状态保存,一般都要实现SaveViewState(),LoadViewState()和TrackViewState()三个方法,这是由IStateManager接口所定义的。 前两个方法作用很明晰,SaveViewState()是将控件的当前状态抽取为一个状态对象,页面类获取所有控件的状态对象对其进行编码生成可在网络上传输的格式(Base64),并将其塞入到一个id为__VIEWSTATE的input元素中发给浏览器。 LoadViewState()是控件从浏览器中传回来的数据中重新读取值,使其回复到上次状态。 最头痛的是TrackViewState(),它是干什么的?MSDN里的话如下: Causes tracking of view-state changes to the server control so they can be stored in the server control's StateBag object. This object is accessible through the Control.ViewState property. 这个话无比正确,但却让人不怎么明白。它怎么就让控件跟踪视图状态的变化了?浏览器回发的数据包含了页面当前控件的值,而__VIEWSTATE隐藏域中包含了上次控件的状态,两者一比不就知道哪些变化了,用你TrackViewState()是干什么? Google了许多资料,几乎就没发现有价值的介绍,最后,终于在一篇文章《TRULY Understanding ViewState 》中发现了有价值的东西。 原来,为了减少在网络上的传输量,应该只保存“变化”的数据到视图状态中。 所有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. 以下为StateBag的TrackViewState()的反汇编代码: internal void TrackViewState() 向ViewState中追加数据的方法本质上是通过StateBag的Add()方法实现的: public StateItem Add(string key, object value) if ((value == null) && !this.marked) //设定已更改标记
internal object SaveViewState()
那么,到底控件的视图状态是怎样保存的?这涉及到页面的生命周期。 当页面被装载时,它的ProcessRequest()方法被调用。在此方法中,会调用一个SaveAllState方法,此方法内部又调用SaveViewStateRecursive()方法(来自基类Control), 综上所述: protected void Page_Load(object sender, EventArgs e) if (!IsPostBack) } 在Page上扔个Button,以便可以PostBack。运行后Postback的结果,“Hello” item没被保留。 protected void Page_Load(object sender, EventArgs e) if (!IsPostBack) } } 这个例子说明:将控件加入到页面类的Controls集合中时,会自动调用TrackViewState()方法。 注意:Control.TrackViewState()方法是保护的,不允许外界调用。而动态创建Button等简单控件时,没有办法在页面类中直接调用TrackViewState()方法。因此,通过Controls.Add()方法间接调用TrackViewState()方法是唯一的选择。 最后给出一条动态创建控件的原则: 应该在new出控件对象之后,马上将其加入到父控件的Controls集合中,这样可以完全地保证它的状态能在回发时恢复。 |