服务器控件开发——ViewState(6)

  在控件开发中ViewState 是一个很重要的概念, 同时也是一个很难搞清楚的东西。 我曾经到网上查看过很多关于View state
的文章,很多都是对于ViewState 的原理进行解释, 很少做深入的探讨0 在我的这篇文章当中,我不想对ViewState 的基本原理再进行冗余的重复,
而是努力写出一点与众不同的地方, 让大家对ViewState 更深一点了解。

 要理解ViewState 必须的搞清楚下面几个问题 :
 1. 什么是ViewState.
 2. Asp.net 如何保存ViewState 以及读取 ViewState
 3. ViewState 保存些什么东西。

下面我们对这是那个问题一一探讨。

1. 什么是ViewState ,他有什么作用 ?
严格来讲, ViewState 是一种机制, 是Asp.Net 保存状态的一种机制,具体来讲ViewState 就是Asp.net 服务端写入到客户端一个 隐藏 字段。

该字段的名字叫做:“ __VIEWSTATE”随便打开一个Asp.net 的page都能看到有一个像这样

<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUKLTM3ODU5NDMxNQ9kFgICAw9kFgICAw8PZGRkZIa1AcwFgqgCeB/r2LaDqCLk/Ihz" />
的隐藏字段。 这个Value 就是Viewstate的值

 

2. Asp.net 是如何保存Viewstate 的?

下面的方法是Page 基类在PreRend 生命周期之后调用的用来保存ViewState 的方法。

 

ContractedBlock.gif ExpandedBlockStart.gif Code
internal void SavePageViewState ()
    {
        
if (!handleViewState)
            
return;

        Pair pair 
= new Pair ();
        pair.First 
= SaveViewStateRecursive ();
        
if (_requiresPostBack != null && _requiresPostBack.Count > 0)
            pair.Second 
= _requiresPostBack;

        
if (pair.First == null && pair.Second == null)
            pair 
= null;

        SavePageStateToPersistenceMedium (pair);
    }

    
protected virtual void SavePageStateToPersistenceMedium (object viewState)
    {
        _savedViewState 
= viewState;
    }

    
//下面的方法在Page的Render方法中被调用,将视图状态数据写入到一个客户端隐藏字段中。
    internal string GetViewStateString ()
    {
        StringWriter sr 
= new StringWriter ();
        LosFormatter fmt 
= new LosFormatter ();
        
        
// 序列化 ViewState 
        fmt.Serialize (sr, _savedViewState);
        
return sr.GetStringBuilder ().ToString ();
    }

 

注意没有, 页面通过调用Control 基类的 SaveViewStateRecursive 来获取页面的Viewstate信息,然后通过LosFormatter 类将其序列化为
一个字符串, 然后写入到客户段。

那么Asp.net 是如何加载客户端的ViewState 的呢?

加载客户端的ViewState 与 写入Viewstate 刚好相反。

ContractedBlock.gif ExpandedBlockStart.gif Code
internal void LoadPageViewState()
    {
        
object sState = LoadPageStateFromPersistenceMedium ();
        
if (sState != null) {
            Pair pair 
= (Pair) sState;
            LoadViewStateRecursive (pair.First);
            _requiresPostBack 
= pair.Second as ArrayList;
        }
    }

    
protected virtual object LoadPageStateFromPersistenceMedium ()
    {
        NameValueCollection postdata 
= _requestValueCollection;
        
string view_state;
        
if (postdata == null || (view_state = postdata ["__VIEWSTATE"]) == null)
            
return null;

        _savedViewState 
= null;
        LosFormatter fmt 
= new LosFormatter ();

        
try {
            _savedViewState 
= fmt.Deserialize (view_state);
        } 
catch (Exception e) {
            
throw new HttpException ("Error restoring page viewstate.\n", e);
        }

        
return _savedViewState;
    }

 

LoadPageStateFromPersistenceMedium  将视图状态反序列化为对象, 然后交给Page 对象的 LoadViewStateRecursive
处理。 

LoadViewStateRecursive 与 SaveViewStateRecursive 的处理逻辑请参考我第一篇的关于Control 基类的博文。简单说来

SaveViewStateRecursive 分别将子控件ID 的集合, 子控件的视图状态数据的集合, 控件本身的视图状态数据(通过调用控件的IStateManger.SaveViewstate 得到)
 保存到一个Tripet 结构当中。然后返回。

LoadViewStateRecursive 首先调用IstateManage.LoadViewState 方法 加载自身的ViewState  然后调用子类的LoadViewStateRecursive
方法加载子类的视图状态。

以前一直有一个疑问: 脱离Asp.net, 我们是否可以通过一个通用的程序来解释ViewState, 并且明白其中的意义, 理论上这是不可行的。
因为控件保存 与读取 自身视图状态的逻辑被抽取成了一个接口IStatemanager接口。 不同的控件保存自身视图状态的方式不一样。
读取的方式也就不一样。 不过假设我们能够猜到页面本身以及其所有子控件的保存控件的视图状态的方式。 那么针对于
特定的页面我们是可以读取其ViewState 的内容的。 事实上, 对于SaveViewState 的实现,大部分控件都采用了基类的实现,
即使自定义实现, 我们也是可以通过查看ViewState 的内容猜测到, 定制这样的一个针对与大部分页面的ViewStateParser 是
可能的。 http://www.codeproject.com/KB/viewstate/viewstate_viewer.aspx 这是我在CodeProject 找到的一篇关于
如何通过自定义程序来Parser 的文章, 有兴趣的朋友可以去看看。


3. ViewState 保存些什么东西

这个问题看似简单, 实则不然, 大部分人的误解也正在这儿。比如说TextBox 控件的Text 属性。 一般都认为 如果启用ViewState 的话 Text 属性
的值会保存到ViewState 中。而事实上并不是这样。在这儿不得不提一下IStatemanager 接口了。该接口实现3个方法和一个属性。
      
 // 检查视图状态跟踪状态。
 bool IStateManager.IsTrackingViewState
        {
            get;
        }
 
 // 加载视图状态。
        void IStateManager.LoadViewState(object state)
        {
            
        }

 // 保存视图状态, 只有在视图状态跟踪开启的时候才会保存, 否则不保存。
        object IStateManager.SaveViewState()
        {
         
        }
 
        // 开启视图状态跟踪, 该方法被调用之后, IsTrackingViewState 属性返回True.
   void IStateManager.TrackViewState()
        {
         
        }

 

在控件的基类中。控件对于IStateManager 的实现被委托给一个StateBag 类型的变量来实现,剩下的就是StateBag魔法了
这个类的实现是很复杂的, 有兴趣的朋友可以通过Google Code Search 搜索出来看一下。在这里我重点介绍一下他的功能。
StateBag 内置了一个Dictionary ,通过这个Dictionary 你可以向StateBag 添加键值对。 同时该对象还为
每一个加入其中的Item 跟踪一个Dirty  状态值, 记住只有状态为Dirty 的键值对才会被序列化到ViewState中。

为了搞清楚Dirty 究竟是怎么回事, 我写了一下小小的程序来测试一下。

ContractedBlock.gif ExpandedBlockStart.gif Code
   public static void Main()
        {
            StateBag bag 
= new StateBag();
            bag.Add(
"flag""abcd");
            Console.WriteLine(bag.IsItemDirty(
"flag"));
            bag.SetItemDirty(
"flag"true);
            Console.WriteLine(bag.IsItemDirty(
"flag"));
    
            bag.Add(
"key""efc");
            Console.WriteLine(bag.IsItemDirty(
"key"));
            ((IStateManager)bag).TrackViewState();
            bag[
"key"= "efg";
            Console.WriteLine(bag.IsItemDirty(
"key"));

            Console.ReadLine();
        }

该程序的输出为:
  false
  True
  false
  false
  True
聪明的你可能已经得出结论: 在 StateBag 开启跟踪视图状态之前, 对于 StateBag 中的键值对来说, 无论是添加, 还是修改其状态始终都是UnDirty状态。
         而在StateBag 开启跟踪视图状态之后,对于StaeBag 中的键值对来说, 添加或者修改,其状态将被修改为Dirty 状态。
Microsoft  为什么要如此设计呢?

 我们已Label 控件为例来说明一下。在生命周期的初始化阶段: Label  控件根据Aspx 页面中的申明初始化控件。 比如一个申明如下的控件 
  <asp:Label ID="Label1" runat="server" Text="myLabel" Width="86px"></asp:Label>
初始化后其Text属性值就被保存在 StateBag 中, 但是这个时候其状态为 Undirty

接着控件开启视图状态跟踪, 在此后的控件生命周期中, 只要用户设置Text 属性的值, 这个状态都会被设置成
Dirty 状态。当然如果用户不设置这个值, 其状态将一直保存UnDirty 状态。通过这样一种方式, Asp.net 保障了
在aspx 页面中申明的属性,初始化必须的属性, 以及在用户在Onint 代码中改变的属性 都不会保存到ViewState 中
从而保证了Viewstate的最小化。
这儿还有一点需要注意的地方(我自己也很迷惑)就是 TextBox  的Text 值无论在什么地方被赋值, 都不会保存到ViewState 中 ,但是如果你使用了TextBox 控件的
TextChange 事件 那么则另当别论, 我想可能是因为TextBox 的Text 已经随PostBack 发送到服务器端,而不需要那么
麻烦再到ViewState 中去取, 这样既减少的ViewState 的大小, 又提高了效率。


回答了上面3 个问题之后, 我想大家对于ViewState 已经有了一个全面的了解。 

转载于:https://www.cnblogs.com/wwwyfjp/archive/2008/09/12/1290082.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值