简单类型视图状态应用

来源:互联网 发布:艺动网络 编辑:程序博客网 时间:2024/06/10 16:54

简单类型视图状态应用

本文节选自《庖丁解牛:纵向切入ASP.NET 3.5控件和组件开发技术》一书


     视图状态默认支持很多类型的数据存储,其中基本类型的有字符串、数字、布尔值、颜色、日期、字节,以及各种类型的数组等。以下是一个最常见的典型用法:
public string Text
{
    get
    {
        String s = (String)ViewState["Text"];
        return ((s == null) ? String.Empty : s);
    }

    set
    {
        ViewState["Text"] = value;
    }
}
在上面代码中有个ViewState的对象,此对象没有多么深奥,只是基类Control中定义的一个属性。追溯到它的基类定义,代码如下:
private StateBag _viewState;
[WebSysDescription("Control_State"), Browsable(false), Designer Serializa
tionVisibility(DesignerSerializationVisibility.Hidden)]
protected virtual StateBag ViewState
{
    get
    {
        if (this._viewState == null)
        {
            this._viewState = new StateBag(this.ViewStateIgnoresCase);
            if (this.IsTrackingViewState)
            {
                this._viewState.TrackViewState();
            }
        }
        return this._viewState;
    }
}
这是一个标准的自定义类型属性。再仔细看一下,该属性的类型为StateBage类,这才是我们要找的关键类,它的代码结构如下:
/// <summary>
/// 获得本书更多内容,请看:
/// http://blog.csdn.net/ChengKing/archive/2008/08/18/2792440.aspx
/// </summary>
public sealed class StateBag : IStateManager, IDictionary, ICollection, IEnumerable
{
    // Fields
    private IDictionary bag;
    private bool ignoreCase;
    private bool marked;

    // Methods
    public StateBag()
        : this(false)
    {
    }

    public StateBag(bool ignoreCase)
    {
        this.marked = false;
        this.ignoreCase = ignoreCase;
        this.bag = this.CreateBag();
    }

    public StateItem Add(string key, object value)
    {
        if (string.IsNullOrEmpty(key))
        {
            throw ExceptionUtil.ParameterNullOrEmpty("key");
        }
        StateItem item = this.bag[key] as StateItem;
        if (item == null)
        {
            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;
    }

    public void Clear()
    {
        this.bag.Clear();
    }

    private IDictionary CreateBag()
    {
        return new HybridDictionary(this.ignoreCase);
    }

    public IDictionaryEnumerator GetEnumerator()
    {
        return this.bag.GetEnumerator();
    }

    public bool IsItemDirty(string key)
    {
        StateItem item = this.bag[key] as StateItem;
        return ((item != null) && item.IsDirty);
    }

    internal void LoadViewState(object state)
    {
        if (state != null)
        {
            ArrayList list = (ArrayList)state;
            for (int i = 0; i < list.Count; i += 2)
            {
                string key = ((IndexedString)list[i]).Value;
                object obj2 = list[i + 1];
                this.Add(key, obj2);
            }
        }
    }

    public void Remove(string key)
    {
        this.bag.Remove(key);
    }

    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;
    }

    public void SetDirty(bool dirty)
    {
        if (this.bag.Count != 0)
        {
            foreach (StateItem item in this.bag.Values)
            {
                item.IsDirty = dirty;
            }
        }
    }

    public void SetItemDirty(string key, bool dirty)
    {
        StateItem item = this.bag[key] as StateItem;
        if (item != null)
        {
            item.IsDirty = dirty;
        }
    }

    void ICollection.CopyTo(Array array, int index)
    {
        this.Values.CopyTo(array, index);
    }

    void IDictionary.Add(object key, object value)
    {
        this.Add((string)key, value);
    }

    bool IDictionary.Contains(object key)
    {
        return this.bag.Contains((string)key);
    }

    void IDictionary.Remove(object key)
    {
        this.Remove((string)key);
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }

    void IStateManager.LoadViewState(object state)
    {
        this.LoadViewState(state);
    }

    object IStateManager.SaveViewState()
    {
        return this.SaveViewState();
    }

    void IStateManager.TrackViewState()
    {
        this.TrackViewState();
    }

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

    // Properties
    public int Count
    {
        get
        {
            return this.bag.Count;
        }
    }

    internal bool IsTrackingViewState
    {
        get
        {
            return this.marked;
        }
    }

    public object this[string key]
    {
        get
        {
            if (string.IsNullOrEmpty(key))
            {
                throw ExceptionUtil.ParameterNullOrEmpty("key");
            }
            StateItem item = this.bag[key] as StateItem;
            if (item != null)
            {
                return item.Value;
            }
            return null;
        }
        set
        {
            this.Add(key, value);
        }
    }

    public ICollection Keys
    {
        get
        {
            return this.bag.Keys;
        }
    }

    bool ICollection.IsSynchronized
    {
        get
        {
            return false;
        }
    }

    object ICollection.SyncRoot
    {
        get
        {
            return this;
        }
    }

    bool IDictionary.IsFixedSize
    {
        get
        {
            return false;
        }
    }

    bool IDictionary.IsReadOnly
    {
        get
        {
            return false;
        }
    }

    object IDictionary.this[object key]
    {
        get
        {
            return this[(string)key];
        }
        set
        {
            this[(string)key] = value;
        }
    }

    bool IStateManager.IsTrackingViewState
    {
        get
        {
            return this.IsTrackingViewState;
        }
    }

    public ICollection Values
    {
        get
        {
            return this.bag.Values;
        }
    }
}
该类继承了四个接口:IStateManager,IDictionary,ICollection,IEnumerable。IStateManager即.NET Framework为自定义视图状态管理提供的接口,到这里您应该明白我们直接使用ViewState对象时其实是隐式用到了IStateManager接口,只不过Control类不是继承IStateManager实现的,而是采用关联对象方式把StateBag类的一个实例作为自己的一个属性保持而已。这样从技术角度能够把IStateManager接口的几个方法与Control对控件生命周期支持的几个同名方法区别开来(它们命名是相同的)。另外,这几个方法在使用上也非常简便,直接通过属性的方式使用,否则使用时就要重写基类的方法实现,显得比较笨重且缺乏灵活性。
后面三个接口IDictionary,ICollection,IEnumerable主要为视图对象的存储集合以及对集合的快速检索提供支持。在这里可以看到我们使用的ViewState在服务端也存储在一个标准的IDictionary类型中,如下:
private IDictionary bag;
IDictionary集合采用键(string类型)/值(object类型)的格式存储。除了bag对象,还有两个内部变量:
private bool ignoreCase;
private bool marked;
ignoreCase指定在集合中存储的键是否忽略大小写。marked变量就标记是否启用了跟踪监控的变量,只有当该值为true时,才把值保存到视图集合对象中,否则如果集合中有该对象就移除它。在Add方法的核心代码片段中体现了这一点,代码如下:
/// <summary>
/// 获得本书更多内容,请看:
/// http://blog.csdn.net/ChengKing/archive/2008/08/18/2792440.aspx
/// </summary>
public StateItem Add(string key, object value)
{
    //… …
    StateItem item = this.bag[key] as StateItem;
    if (item == null)
    {
        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;
}
这一段代码比较严谨,除了判断marked是否为true,还判断要增加的对象是否为null如果为null,也不会增加到视图集合列表对象中。另外,在视图集合中,对应的值类型为StateItem,它的代码如下所示:
/// <summary>
/// 获得本书更多内容,请看:
/// http://blog.csdn.net/ChengKing/archive/2008/08/18/2792440.aspx
/// </summary>
public sealed class StateItem
{
    // Fields
    private bool isDirty;
    private object value;

    // Methods
    internal StateItem(object initialValue);

    // Properties
    public bool IsDirty { get; set; }
    public object Value { get; set; }
}
在这里除了定义了存储数据内容的object对象的value属性外,还有一个Dirty属性,该属性值标志当前集合中的一个对象是否是脏数据(即被改动过了),SaveViewState方法只对脏数据进行保存,以便提高性能。SaveViewState的代码片段如下:
/// <summary>
/// 获得本书更多内容,请看:
/// http://blog.csdn.net/ChengKing/archive/2008/08/18/2792440.aspx
/// </summary>
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;
   }
代码体中的语句if(item.IsDirty)就是对要保存的序列化对象进行过滤,最终返回的list集合对象中的item的Dirty属性值都为true。
StateBag类的关键点就介绍这些。StateBag是.NETFramework提供的一个比较实用的类,并且它实现了IStateManager,可以作为自定义类型视图、状态的一个典型例子,在实现自己的视图状态类时完全可以参考它。在实际开发时,很多情况下也并非一定要显示继承 IStateManager接口,系统类有些类型继承了IStateManager,比如Style,这样我们可以直接拿来使用,还有它的一些派生类TableItemStyle,TableStyle,PanelStyle 都可以直接在控件开发中使用。后面会介绍一个使用TableItemStyle作为基类实现自定义类型视图状态的示例。