前言:
\n
三易其名,是因为根据网友的建议来了。所以改成了asp.net控件开发。呵呵,不瞎扯了,上一节讲到了复合控件的基础概念,今天就深入学习复合控件视图是如何跟踪子控件状态的。是如何保存子控件状态的呢,是如何加载子控件状态的呢。
\n
正文:
\n
带着这些问题,进入我们的学习环节,从上面的几节中,我知道了解control类中有内建用来跟踪、保存和恢复子控件的状态。我们也知道复合控件也是继承于control(controlàWebControlà自定义复合控件)而来的。在开始跟踪视图状态阶段中,Control依次调用Controls 集合中控件的TrackViewState方法,开始跟踪子控件的状态。但是,如果某个子控件是在父控件已经打开跟踪状态后被添加到Controls 集合中的,那么子控件的TrackViewState方法在它被添加到控件树的同时被调用。
\n
我们在创建复合控件时。有这样的一个重写方法。
Protected override void CreatedChildControls()
\n
{
\n
//………
\n
This.Controls.Add(…);
\n
//………
}
\n
在这个重写的方法中调用了ControlCollection中Add方法(Controls.Add)。
\n
其伪代码为:
\n
public virtural void Add(Control child)
\n
{
\n
………
\n
Int index=this._size;
\n
this._size++;
\n
this._owner.AddedControl(control,index);//_owner是Control类,在//这里调用了 Control内部方法AddedControl;
\n
……..
\n
}
\n
说到这里,我们有必要来看看AddedControl方法的内部实现。AddedControl(Control control, int
\n
index)方法,将子控件添加到容器控件中。 在AddedControl中会对子控件的ViewState做一些处理。
\n
在此时,加载视图状态。调用了每个子控件的TrackViewState()这个方法 AddedControl伪代码:
\n
protected internal virtual void AddedControl(Control control, int index)
\n
{ ….
\n
control.LoadViewStateRecursive(savedState); //这里开始跟踪视图状态,添加入statebag中
\n
}
\n
在(savestateview)保存视图状态阶段时,Control先调用SaveViewState 方法。默认情况下,该方法调用
\n
ViewState字典的SaveView。并保存返回的对象。作为控件视图状态的第一部分。Control接下来对控件树中的
每个子控件调用SaveViewState .如果返回的子控件状态不为空。那么Control在两个ArrayList中保存该控件的
编号(在Controls集合中)用来串行化。这些子控件的状态不为空,而第二个ArrayList保存了子控件所保存的状
态。最后,在该阶段的最后,Control返回它的三部分视图状态。
\n
整个复合控件树的ViewState是怎么保存和读取的呢?
在Control内部有两个internal的方法:
internal object SaveViewStateRecursive();
internal void LoadRecursive();
这两个方法由调用Control的SaveViewStateRecursive()方法,这个方法就是通过递归调用每一个
\n
Control.Controls的SaveViewStateRecursive方法来保存控件树中所有控件的ViewState。到这里,可能聪明的朋友要问了,既然SaveViewStateRecursive是递归调用保存的方法,那么我们上面写的SaveViewState()方法又有什么用呢?
我们知道,Control.Controls可能会有很多个,而且我们的SaveViewState()只保存了当前控件的数据,而没
\n
有记录控件树的结构,那么如果我们递归SaveViewState()方法来保存数据的话,那么控件树的结构就会丢失,
那么Load的时候就没办法还原了,实际上在SaveViewStateRecursive方法中大致的代码是这样:
[1] 获取控件自己的ViewState(调用SaveViewState方法)
[2] 循环子控件
{
定义两个动态数组,一个保存控件的索引,一个保存递归调用子控件SaveViewStateRecursive方法返回的值
}
[3] 定义一个Triplet // Third中保存的是当前页面需要PostBack的控件名的列表,Triplet这个类型,实际上这个类在MSDN中查一下得到,它就是一个包含了三个对象的对象,有三个属性:First、Second、Thrid
[4] First保存本控件的ViewState
[5] Second保存子控件的索引
[6] Third保存递归子控件SaveViewStateRecursive方法的返回值
[7] 返回Triplet
\n
这样就保存了整个控件树的ViewState和控件树的结构
Load的方式与Save差不多,只是Load的时候会从savedState中获取子控件的索引来依次递归子控件的
\n
LoadRecursive()方法,这样才能保证正确的把保存的数据传给子控件
\n
在(loadViewState)加载视图状态时,Control执行了它在保存视图状态阶段中执行的相返操作。页面在
该阶段传给Control的状态和控件在上一次请求的最后保存的状态一致。Control通过调用LoadViewState方法来
加载所保存状态的第一部分,该方法依次把保存的状态加载入ViewState字典。接下来,Control访问Controls
集合,把其余的状态加载子控件,那么其剩下的状态将由代表子控件编号和保存状态的ArrayList组成。Control用
这些编号和状态把状态加载入子控件,这些子控件的状态是在前一次请求的最后保存的,这样。就完成了控件及
其子控件状态恢复。如果在该阶段还没有创建子控件,那么Control保存子控件的状态,留做以后使用。接下来当
子控件被创建和添加到控件树时,Control把状态载入子控件。注意。视图状态跟踪是在加载视图状态阶段之前打
开的。因此,通过使用视图状态自动恢复的任何控件属性都标记起来。并且在保存视图状态阶段中重新保存在视
图状态中。回传时,这些属性在加载视图状态阶段重新加载,因此视图状态机制在接下来的请中被持续保存。
\n
刚刚在每个状态管理阶段讲解的步骤都是在控个把树中递归执行的。因些控件的视图状态代表了控件层次的
组合视图状态。 Control用Controls集合中子控件的编号来标识子控件的已保存的视图状态。Control并没有用
到子控件的类型或ID,这样能达到更高的性能。因为它减少了视图状态的大小。但是。这意味着视图状态机制只
有当控件树在加传重建军时采用的次序跟在前一次请法度的最后视图状态保存次序是一到的情况下才能工作。可
能通过重载Controls属性来维持CreateChildControls中控件创建次序。
\n
例如:
\n
Public override ControlCollection controls{
\n
get{
\n
EnsureChildControls();
\n
Return base.Controls;
\n
}
\n
}
\n 这段代码将使在CreateChildControls中创建的子控件在由用户代码添加其他控件之前被添加控件树,因此。
在CreateChildControls中创建的子控件回传时按保存顺序被重新创建。这样页面框可以通过使用视图状态来恢
复它们的状态。