让人糊涂的TrackViewState()与视图状态保存

2024-01-24 05:08

本文主要是介绍让人糊涂的TrackViewState()与视图状态保存,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

让人糊涂的TrackViewState()与视图状态保存

在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 》中发现了有价值的东西,它的网址如下:   http://weblogs.asp.net/infinitiesloop/archive/2006/08/03/Truly-Understanding-Viewstate.aspx

原来,为了减少在网络上的传输量,应该只保存“变化”的数据到视图状态中。

所有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集合中,这样可以完全地保证它的状态能在回发时恢复。

============================
为了弄明白这点小东西,先后看了MSDN无数遍,Google了N篇文章,又用Reflector反汇编,累晕了。

再次感叹,ASP.NET框架内部的机制太复杂太庞大了,有的时候,还真是得“不求甚解”,不然,要弄清楚所有底层技术细节,恐怕胡须都白了。

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/bitfan/archive/2008/06/02/2504743.aspx

这篇关于让人糊涂的TrackViewState()与视图状态保存的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/638614

相关文章

MySQL 中的服务器配置和状态详解(MySQL Server Configuration and Status)

《MySQL中的服务器配置和状态详解(MySQLServerConfigurationandStatus)》MySQL服务器配置和状态设置包括服务器选项、系统变量和状态变量三个方面,可以通过... 目录mysql 之服务器配置和状态1 MySQL 架构和性能优化1.1 服务器配置和状态1.1.1 服务器选项

linux进程D状态的解决思路分享

《linux进程D状态的解决思路分享》在Linux系统中,进程在内核模式下等待I/O完成时会进入不间断睡眠状态(D状态),这种状态下,进程无法通过普通方式被杀死,本文通过实验模拟了这种状态,并分析了如... 目录1. 问题描述2. 问题分析3. 实验模拟3.1 使用losetup创建一个卷作为pv的磁盘3.

使用C++将处理后的信号保存为PNG和TIFF格式

《使用C++将处理后的信号保存为PNG和TIFF格式》在信号处理领域,我们常常需要将处理结果以图像的形式保存下来,方便后续分析和展示,C++提供了多种库来处理图像数据,本文将介绍如何使用stb_ima... 目录1. PNG格式保存使用stb_imagephp_write库1.1 安装和包含库1.2 代码解

Java实现状态模式的示例代码

《Java实现状态模式的示例代码》状态模式是一种行为型设计模式,允许对象根据其内部状态改变行为,本文主要介绍了Java实现状态模式的示例代码,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来... 目录一、简介1、定义2、状态模式的结构二、Java实现案例1、电灯开关状态案例2、番茄工作法状态案例

通过prometheus监控Tomcat运行状态的操作流程

《通过prometheus监控Tomcat运行状态的操作流程》文章介绍了如何安装和配置Tomcat,并使用Prometheus和TomcatExporter来监控Tomcat的运行状态,文章详细讲解了... 目录Tomcat安装配置以及prometheus监控Tomcat一. 安装并配置tomcat1、安装

Linux之进程状态&&进程优先级详解

《Linux之进程状态&&进程优先级详解》文章介绍了操作系统中进程的状态,包括运行状态、阻塞状态和挂起状态,并详细解释了Linux下进程的具体状态及其管理,此外,文章还讨论了进程的优先级、查看和修改进... 目录一、操作系统的进程状态1.1运行状态1.2阻塞状态1.3挂起二、linux下具体的状态三、进程的

vscode保存代码时自动eslint格式化图文教程

《vscode保存代码时自动eslint格式化图文教程》:本文主要介绍vscode保存代码时自动eslint格式化的相关资料,包括打开设置文件并复制特定内容,文中通过代码介绍的非常详细,需要的朋友... 目录1、点击设置2、选择远程--->点击右上角打开设置3、会弹出settings.json文件,将以下内

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

hdu1565(状态压缩)

本人第一道ac的状态压缩dp,这题的数据非常水,很容易过 题意:在n*n的矩阵中选数字使得不存在任意两个数字相邻,求最大值 解题思路: 一、因为在1<<20中有很多状态是无效的,所以第一步是选择有效状态,存到cnt[]数组中 二、dp[i][j]表示到第i行的状态cnt[j]所能得到的最大值,状态转移方程dp[i][j] = max(dp[i][j],dp[i-1][k]) ,其中k满足c

状态dp总结

zoj 3631  N 个数中选若干数和(只能选一次)<=M 的最大值 const int Max_N = 38 ;int a[1<<16] , b[1<<16] , x[Max_N] , e[Max_N] ;void GetNum(int g[] , int n , int s[] , int &m){ int i , j , t ;m = 0 ;for(i = 0 ;