在ASP.NET自定义控件开发中,如果需要保存控件的状态,通常都需要实现SaveViewState(),LoadViewState()和TrackViewState()三个方法,这是由IStateManager接口所定义的。

前两个方法作用很明晰,SaveViewState()是将控件的当前状态抽取为一个状态对象,页面类获取所有控件的状态对象对其进行编码生成可在网络上传输的格式(Base64),并将其塞入到一个id为__VIEWSTATE的input元素中发给浏览器。LoadViewState()是控件从浏览器中传回来的数据中重新读取值,使其回复到上次状态。

那么TrackViewState(),它是干什么的?

它怎么就让控件跟踪视图状态的变化了?浏览器回发的数据包含了页面当前控件的值,而__VIEWSTATE隐藏域中包含了上次控件的状态,两者一比不就知道哪些变化了,用你TrackViewState()是干什么?原来,为了减少在网络上的传输量,应该只保存“变化”的数据到视图状态中。所有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.
private bool marked;

以下为StateBag的TrackViewState()的反汇编代码:

internal void TrackViewState()
{
    this.marked = true;
}

向ViewState中追加数据的方法本质上是通过StateBag的Add()方法实现的:

public StateItem Add(string key, object value)
{
 //Key不能为空
    if (string.IsNullOrEmpty(key))
    {
        throw ExceptionUtil.ParameterNullOrEmpty("key");
    }
    //根据Key查找集合中的数据对象
    StateItem item = this.bag[key] as StateItem;
    if (item == null)  //没有找到对应的数据对象
    {
        //如果传入的value不为空或要求跟踪,则创建一个对象加入到集合中
        if ((value != null) || this.marked)
        {
            item = new StateItem(value);
            this.bag.Add(key, item);
        }
    }
    else 
     //如果找到对应的数据对象

if ((value == null) && !this.marked)
          {
              this.bag.Remove(key); //值为空,且不要求跟踪,则从集合中移除此对象
           }
          else  //要求跟踪或者值不为空,设置对应的数据对象值
          {
              item.Value = value;
           }

//设定已更改标记
    if ((item != null) && this.marked)
    {
        item.IsDirty = true;
    }
    return item;
}

可以清楚地看到,如果标记被设置,IsDirty属性就返回true,否则,保持为false.
注意:这里并没有比对原始值和传入的值是否相等再设置IsDirty属性。因此,只要标记被设置,任何对视图状态的非空赋值都被认为是Dirty的。
 
因此,TrackView()方法其实就是设置了一个“请监控我的变化”的标记,调用此方法之后,任何对控件属性的改变都会被跟踪,这样一来,此控件的SaveViewState()方法在生成状态对象时就会将此属性的修改记录下来。简单地说:只有IsDirty=true的属性值才会被SaveViewState()方法处理。这就避免了为控件所有的属性都生成状态数据,大大减少了要保存的数据量。
以下为StateBag类的SaveViewState()反汇编代码,可以清楚地看到其中使用了IsDirty属性。

internal object SaveViewState()
{
    ArrayList list = null;
    if (this.bag.Count != 0)
    {
        IDictionaryEnumerator enumerator = this.bag.GetEnumerator();
        while (enumerator.MoveNext())
        {
            StateItem item = (StateItem) enumerator.Value;
            if (item.IsDirty)
            {
                if (list == null)
                {
                    list = new ArrayList();
                }
                list.Add(new IndexedString((string) enumerator.Key));
                list.Add(item.Value);
            }
        }
    }
    return list;
}

一切都清楚了。

那么,到底控件的视图状态是怎样保存的?这涉及到页面的生命周期。

当页面被装载时,它的ProcessRequest()方法被调用。在此方法中,会调用一个SaveAllState方法,此方法内部又调用SaveViewStateRecursive()方法(来自基类Control),
SaveViewStateRecursive()先调用Control.SaveViewState()保存自己的数据,再递归地调用每个子控件的SaveViewStateRecursive()方法获取所有子控件的状态对象,然后一级级返回,最终得到整个页面的状态对象,紧接着将这一对象按Base64编码生成页面视图状态字串并放入到__VIEWSTATE隐藏域中(由Page.SavePageStateToPersistenceMedium方法完成)。

综上所述:
如果控件没调用TrackView()方法,那么,本次对控件属性的修改将不会被添加到__VIEWSTATE隐藏域中,因此,下次页面回发时,控件的属性将回复为默认值。
理解这点还是有意义的,特别是在动态创建控件的情况下,请看以下这个典型示例:

protected void Page_Load(object sender, EventArgs e)
    {
      
        CheckBoxList chk = new CheckBoxList();

if (!IsPostBack)
        {
            chk.Items.Add("Hello");
        }
        form1.Controls.Add(chk);

}

在Page上扔个Button,以便可以PostBack。运行后Postback的结果,“Hello” item没被保留。
改为:

protected void Page_Load(object sender, EventArgs e)
    {
      
        CheckBoxList chk = new CheckBoxList();
        form1.Controls.Add(chk);

if (!IsPostBack)
        {
            chk.Items.Add("Hello");
        }

}
或者:
 protected void Page_Load(object sender, EventArgs e)
    {
      
        CheckBoxList chk = new CheckBoxList();
        (chk.Items as IStateManager).TrackViewState();
        if (!IsPostBack)
        {
            chk.Items.Add("Hello");
        }
        form1.Controls.Add(chk);

}
都可以在多次回发时保证Hello项出现,并正确地恢复它的状态(选中还是不选中)。

这个例子说明:将控件加入到页面类的Controls集合中时,会自动调用TrackViewState()方法。

注意:Control.TrackViewState()方法是保护的,不允许外界调用。而动态创建Button等简单控件时,没有办法在页面类中直接调用TrackViewState()方法。因此,通过Controls.Add()方法间接调用TrackViewState()方法是唯一的选择。

最后给出一条动态创建控件的原则:

应该在new出控件对象之后,马上将其加入到父控件的Controls集合中,这样可以完全地保证它的状态能在回发时恢复。

转载于:https://www.cnblogs.com/jsping/archive/2012/09/17/2688223.html

TrackViewState到底是干什么的相关推荐

  1. python可以干嘛知乎-Python到底可以干什么?老男孩Python视频教程

    如果说挑选一门编程语言进行学习,你会选择哪个?当然是Python.Python是一门简单的编程语言,适合初学者学习,也是很多人都喜欢的语言,那么Python到底可以干什么?跟着老男孩教育来看看吧. P ...

  2. python主要是干什么用的-Python到底可以干什么?主要应用领域

    如果说挑选一门编程语言进行学习,你会选择哪个?当然是Python.Python是一门简单的编程语言,适合初学者学习,也是很多人都喜欢的语言,那么Python到底可以干什么? Python语言在学术上是 ...

  3. python到底能干啥-Python到底可以干什么?主要应用领域

    如果说挑选一门编程语言进行学习,你会选择哪个?当然是Python.Python是一门简单的编程语言,适合初学者学习,也是很多人都喜欢的语言,那么Python到底可以干什么? Python语言在学术上是 ...

  4. python到底是啥_Python语言中的__init__到底是干什么的?

    本文主要向大家介绍了Python语言中的__init__到底是干什么的?通过具体的内容向大家展示,希望对大家学习Python语言有所帮助. 看到Python中有个函数名比较奇特,__init__我知道 ...

  5. 从TP-Link到雷蛇,纷纷入局智能手机业到底想干什么?

    "眼看他起朱楼,眼看他宴宾客,眼看他楼塌了",这句形容世态炎凉的话其实与智能手机市场更为相像.诺基亚的辉煌与没落.黑莓的强势与消声无迹.摩托罗拉的数次易手.小米的横空出世与崛起.苹 ...

  6. 来客码到底是干什么的?

    重庆码码电子技术开发有限公司,是一家互联网科技技术开发公司,成立于2014年,注册资金1000万,位于重庆直辖市渝北区动力国际. 公司主要从事专业营销软件开发,扫码支付.公众号二次开发.企业商业模式设 ...

  7. 看看外贸业务员到底是干什么的?

    编者按:所谓知己知彼,方能百战不殆.无论是面临就业的大学生,还是考虑再择业的职场人,都离不开一个清晰的职场目标并为之不懈的努力.小编在这里对各行业进行信息梳理,希望对您有所帮助. 外贸业务员,工作挑战 ...

  8. 运维到底是干什么的?看完这篇你就懂了

    运维到底是干什么的?估计连运维工程师本身都不清楚,在百度上搜索也基本得不到答案,找了很多的运维老员工,终于总结出了运维工程师的工作内容: 一般来说,运维工程师都是说的互联网企业的运维师,通常属于技术部 ...

  9. 构造函数到底是干什么的?

    "构造函数到底是干什么的?",构造函数是初始化已创建好的对象中成员变量的,而不是创建对象的,不是,绝对不是,构造函数只是进一步为已创建的对象属性赋值对,使之更像"现实生活 ...

最新文章

  1. freemarker模板文件中文本域(textarea)的高度自适应实现
  2. YTKNetwork使用application json方式传递参数
  3. SaaS平台只是传统管理软件的试衣间
  4. Java DCL 单例模式真的需要对变量加 Volatile 吗?
  5. 我们真的仍然需要32位JVM吗?
  6. UOJ #188. 【UR #13】Sanrd
  7. python迭代计算_Python递归和迭代
  8. 最简单OGG配置方式
  9. python遍历文件_python3 遍历文件夹目录所有文件
  10. 自己动手写Docker系列 -- 3.1构造实现run命令版本的容器
  11. AutoMapper搬运工之初探AutoMapper
  12. 不要再闲玩啦!博主手把手带你两个月入门自然语言处理,还不赶紧碗里来!...
  13. 基于AE+C#读取文件及图层输出
  14. bt种子php啥格式的,bt种子是什么意思(bt种子的格式及文件结构)
  15. 计算机关机界面设置在哪里,windows 7 如何设置自动开关机
  16. 北京中国石油大学计算机考研分数线,中国石油大学(北京)2018年考研复试基本分数线...
  17. -XX:+UseParallelGC与 -XX:+UseParNewGC 区别
  18. 币圈小白必读的6本加密货币书籍
  19. Htmlunit 使用总结
  20. metaspolit提示Exploit failed: You must select a target.

热门文章

  1. pyecharts第六节、水球图
  2. MMM for MySQL FAQ
  3. 使用Oracle的审计功能监控数据库中的可疑操作
  4. Java程序员必备 : Java反编译神器——“GUI” 资源分享
  5. 配置启动MySQL的Docker容器
  6. VS IISExpress REST DELETE 405 Method Not Allowed
  7. 一图解明Android Studio项目文件结构各部分作用
  8. python 的时间模块使用
  9. PHP学习:文件操作
  10. 给出一个包含n个整数的数列,问整数a在数列中的第一次出现是第几个。