我开发自定义控件时写一个连字符型复杂属性主控件添加复杂属性:
        
        [Category("Magic属性")]
        //[Localizable(true)]
        [Description("设置按钮内容区域的内容和样式")]
        [PersistenceMode(PersistenceMode.Attribute)]//用破折号链接的复杂属性
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        [NotifyParentProperty(true)]
        public ContentArea ContentArea
        {
            get
            {
                if (ViewState["ContentArea"] != null)
                {
                   return (ContentArea)ViewState["ContentArea"];
                }
                else
                {
                   return new ContentArea();
                }
            }
            
            
        }ContentArea自定义类:
[TypeConverter(typeof(ExpandableObjectConverter))] 
    public class ContentArea
    {   
  
      ....
 
     }
则在属性浏览器中设置其子属性会造成VS2008崩溃失去响应如果我不用ViewState而采用私有变量的方式去储存复杂属性,即:
        private ContentArea L_ContentArea;
        [Category("Magic属性")]
        //[Localizable(true)]
        [Description("设置按钮内容区域的内容和样式")]
        [PersistenceMode(PersistenceMode.Attribute)]//用破折号链接的复杂属性
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        [NotifyParentProperty(true)]
        public ContentArea ContentArea
        {
            get
            {
                if (L_ContentArea == null)
                {
                   L_ContentArea = new ContentArea();
                }
                return L_ContentArea;
            }
            
            
        }则可正常设置其子属性。问题是:为什么对于复杂属性不能使用ViewState去序列化?对于简单属性我经常都是使用ViewState的。
请各位高手告诉我原因!感激不尽!

解决方案 »

  1.   

    自定义视图状态管理  在介绍视图状态时,我们曾经提到过:对于简单属性,例如,String、Int等类型,.NET执行引擎将自动启用默认视图状态管理机制,以便完成相应的功能。然而,如果开发人员在ViewState中保存的是自定义数据类型,或者需要实现自定义方式优化视图状态管理时,则必须实现自定义视图状态管理。  实现自定义视图状态管理可以通过两种方法。方法一:实现System.Web.UI命名空间中的IStateManager接口成员,其中包括IsTrackingViewState属性和TrackViewState、SaveViewState和LoadViewState方法。这种方法主要是针对自定义数据类型的视图状态管理的情况。方法二:重写Control基类的3个视图状态管理方法:TrackViewState、SaveViewState和LoadViewState。这些方法与IStateManager接口定义的3个方法名称一致。这种方法主要用于通过自定义方式优化默认视图状态管理的情况,其主要目的在于提高效率和性能。掌握以上两种实现方法的捷径是,必须深刻理解.NET框架内部实现视图状态管理的过程。下面两小节内容都是有关内部实现方法的介绍。每一节中均有实现代码,实际就相当于实例代码。所有服务器控件的自定义视图状态管理的实现都不会偏离那些代码所表达的逻辑。当读者真正掌握了那些内部实现方法,那么自定义视图状态管理的实现方法也就迎刃而解了。   1、实现基于IStateManager接口的自定义视图状态管理  对于复杂属性而言,多数需要实现自定义视图状态管理,其关键是实现System.Web.UI.IStateManager接口中定义的方法和属性。下面列举了IStateManager接口定义代码。
    public interface IStateManager{ bool IsTrackingViewState {get;} void LoadViewState(object state); object SaveViewState(); void TrackViewState();}  如上代码所示,IStateManager接口要求类实现IsTrackingViewState属性,以及LoadViewState、SaveViewState和TrackViewState方法。IsTrackingViewState属性定义,当由类实现时,获取一个布尔值,通过该值指示服务器控件是否正在跟踪其视图状态更改。如果服务器控件正在跟踪其视图状态更改,则为true;否则为false。SaveViewState方法定义,当由类实现时,将服务器控件的视图状态更改保存到Object中。LoadViewState方法定义,当由类实现时,加载服务器控件以前保存的控件视图状态,其中的参数state表示包含控件保存的视图状态值的Object。TrackViewState方法定义,当由类实现时,指示服务器控件跟踪其视图状态更改。  ViewState属性与IStateManager接口之间存在密切联系。ViewState属性的类型是StateBag类,StateBag类通过实现IStateManager接口中定义的方法和属性来参与状态管理。其实现过程如下。
    public sealed class StateBag : IStateManager, IDictionary,ICollection, IEnumerable{ 
     private bool _isTrackingViewState;
     private ArrayList _keys;
     private ArrayList _values; 
     private StateItem _item; 
     bool IStateManager.IsTrackingViewState { 
      get { return _isTrackingViewState; } 
     }
     void IStateManager.TrackViewState() { 
      _isTrackingViewState = true;
     }
     object IStateManager.SaveViewState() {
      _keys = new ArrayList();
      _values = new ArrayList(); 
      IDictionaryEnumerator myDirctionaryEnumerator = this.GetEnumerator(); 
      while(myDictionaryEnumerator.MoveNext()) { 
       if(this.Item[(String)myDictionaryEnumerator.Key].IsDirty) {
        _keys.Add(myDictionaryEnumerator.Key); 
        _values.Add(myDictionaryEnumerator.Value); 
       }
      }
      if(_keys.Count>0) { 
       return new Pair(_keys,_values);
      }
     }
     void IStateManager.LoadViewState(object savedState) {
      if(savedState is Pair) {
       _keys = (ArrayList)tempP.First;
       _values = (ArrayList)tempP.Second; 
       IDictionaryEnumerator myDirctionaryEnumerator = this.GetEnumerator(); 
       while(myDictionaryEnumerator.MoveNext()) { 
        for(int j=0;j<_keys.Count;j++)
        {
         if((String)myDictionaryEnumerator.Key == _keys[j].ToString()); 
         {
          this.Item[_keys[j].ToString()].Value = (object)_values[j]; 
         }
        }
       }
      }
     }
    }  请读者注意:以上代码为示意性代码,并非严格意义上的实现代码。在此列出,主要是用于说明StateBag类实现IStateManager接口的逻辑过程。  通过上面的代码,我们可以看到:  (1)在IsTrackingViewState属性中,将该属性设置为只读,并且使用私有变量_isTrackingViewState。  (2)在TrackViewState方法中,把IsTrackingViewState属性使用的私有变量_isTrackingViewState设置为true,这指示系统当某个StateItem添加到StateBag中,或者某个StateItem值被修改时,StateBag类就会自动将该StateItem标记为修改过即添加dirty标记。  (3)在SaveViewState方法中,循环StateBag中的每个StateItem,如果该StateItem被标记为dirty,那么就将其键和值分别添加到两个ArrayList中,并返回该对象。  (4)在LoadViewState方法中,执行了与SaveViewState方法相反的操作。首先将savedState对象分解为两个保存有键和值的ArrayList,然后将其中的值加载到相应的StateItem对象中。  以上就是ViewState属性实现IStateManager接口的基本过程。所有的视图状态管理过程,都要使用以上的实现过程,因此理解以上逻辑对于深入掌握自定义视图状态管理机制具有举足轻重的作用。  2、实现基于Control基类的自定义视图状态管理  如果开发人员需要优化默认视图状态管理机制,以提高控件运行效率和性能,那么必须理解Control基类中默认视图状态管理机制。通过掌握这个管理机制,可以模仿其处理过程以实现自定义视图状态管理。   实现基于Control基类的自定义视图状态管理,需要开发人员实现3个方法:LoadViewState、SaveViewState和TrackViewState。它们与上一小节中介绍的IStateManager接口成员方法同名,并且在方法意义上也基本相同。在此就不对这3个方法多做说明了。  Control基类中的默认视图状态管理机制定义了一个StateBag类型的ViewState属性,并将视图状态管理的任务委托给它。下面请看Control基类的默认状态管理的实现逻辑。private StateBag _viewState;
    protected virtual StateBag ViewState{
     get {
      if(_viewState != null) 
      {
       return _viewState;
      }
      _viewState = new StateBag(ViewStateIgnoresCase);
      if(IsTrackingViewState) 
       _viewState.TrackViewState();
       return _viewState;
     }
    }protected virtual void TrackViewState(){ 
     if(_viewState != null) { 
      _viewState.TrackViewState();
     }
     return null;
    }protected virtual object SaveViewState(){
     if(_viewState != null) {
      _viewState.SaveViewState(); 
     }
     return null;
    }
    protected virtual void LoadViewState(object savedState){
     if(savedState != null) { 
      ViewState.LoadViewState(savedState); 
     }
    }  从上面的代码可以看出:ViewState属性是StateBag类型,当_viewState不为null时,则返回_viewState;当_viewState为null时,则初始化一个StateBag类型的变量_viewState,并判断控件是否正在跟踪其视图状态更改,如果服务器控件正在跟踪其视图状态更改,那么就调用TrackViewState方法开始状态跟踪,最后返回_viewState。另外,在TrackViewState、SaveViewState、LoadViewState方法中,均使用了StateBag类中有关视图状态管理的方法。  在Control基类的默认视图状态管理过程中,由于定义了ViewState属性为StateBag类型,所以必然使用上文中StateBag类实现视图状态管理的逻辑。如果让Control基类实现IStateManager接口中的方法和属性,那么其实现过程必然与StateBag类实现IStateManager接口大同小异,这必然将造成重复,由此可能造成.NET框架改变IStateManager接口的访问性质。此外,在进行自定义视图状态管理的过程中,可能出现StateBag类型与Control基类的视图状态管理机制的冲突,那样就可能产生混乱。而采用目前的这种方式,无论从灵活性、继承性、可复用性,以致从编程人员的习惯上,都具有很多优点。 
      自定义控件状态管理  视图状态与控件状态在数据管理方面不太相同。ASP.NET 2.0在支持针对简单属性的默认视图状态管理机制的同时,还支持自定义视图状态管理。然而,对于控件状态管理而言,则不存在默认控件状态管理机制。开发人员必须实现自定义控件状态管理过程。本小节介绍一下自定义控件状态管理的实现方法,这对于实现基于控件状态功能的对象有着重要意义。  实际上,在前面系列文章介绍控件状态的过程中,读者已经接触了控件状态实现的内容,其中就包括自定义控件状态管理的实现。这个过程的实现与基于Control基类的自定义视图状态管理非常相似,二者都需要重写Control基类中的方法。实现自定义控件状态管理需要重写Control基类的SaveControlState和LoadControlState。下面列举了一段简单示例代码。public class Sample : Control { 
     private int currentIndex = 0; 
     protected override void OnInit(EventArgs e) {
      Page.RegisterRequiresControlState(this);
      base.OnInit(e); 
     }
     protected override object SaveControlState() {
      return currentIndex != 0 ? (object)currentIndex : null;
     }
     protected override void LoadControlState(object state) { 
      if (state != null) { currentIndex = (int)state; }
     }
    }  实现自定义控件状态管理分为3个关键步骤:  (1)调用RegisterRequiresControlState方法。该方法用于将自定义控件注册为具有持久性控件状态的控件。  (2)重写SaveControlState方法。该方法用于保存自页回发到服务器后发生的任何服务器控件状态更改。  (3)重写LoadControlState方法。该方法用于从SaveControlState方法保存的上一个页请求还原控件状态信息。
      

  2.   

    往ViewState中放入自定义类型对象,只需要一个东西:为你的自定义对象类型写上 SerializableAttribute 标签。大部分时候只要花5秒钟写上这个Attribute就可以了。偶尔,如果你的对象类型的某些属性非常复杂,你还可以为你的对象类型实现 ISerializable 接口来告诉asp.net如何序列化、反序列化。当你看到一大堆似是而非的关于状态保存的东西,要知道asp.net有很多套方法保存状态。但是你所问的问题,跟那些没有关系。只要这个简单的条件就可以了。如果不相信,给你写个简单demo:1. 增加一个.cs文件,内容:using System;[Serializable]
    public class MyTestClass
    {
        public string Name { get; set; }
        public long Number { get; set; }
        public byte[] Image { get; set; }
    }
    2. 测试aspx页面,内容:<%@ Page Language="C#" %><script runat="server">
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack)
            {
                var x = new MyTestClass { Name = "he", Number = 109, Image = new byte[] { 0x4d, 0x55, 0xfa } };
                ViewState["x"] = x;
            }
            else
            {
                var x = ViewState["x"];
                int i = 0;  //在这里设置一个调试断点,然后调试,查看这个x变量的内容。
            }
        }
    </script><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head runat="server">
        <title></title>
    </head>
    <body>
        <form id="form1" runat="server">
        <asp:Button ID="Button1" runat="server" Text="Button" />
        </form>
    </body>
    </html>
      

  3.   

    通常很“复杂”的自定义对象,放入ViewState你只需要5秒钟的操作就OK,我有好几年、好几个商品化产品(连锁企业跨多个省市同时访问的MIS系统)的实现基础。在开发这些应用时,我对那些繁杂的asp.net其它实现状态保存的做法,其实根本没有闲工夫去看。
      

  4.   

    控件状态实际上是一种特殊的视图状态,它仍然保存在客户端的隐藏域中,但是它并不会受视图状态启用/禁用的影响,也就是说,即使将ViewState禁用,运行时仍然能正确的恢复在控件状态中保存的数据。使用控件状态
    向页面注册使用控件状态 
    在控件状态保存事件(Control类的SaveControlState方法)中保存相关数据 
    在控件状态读取事件(Control类的LoadControlState方法)中读取保存的数据
    实现ISerializable接口
    参考
    参考 
      

  5.   

    另外, 主要好像是问的 连字符复杂属性实现方法. 请看http://blog.csdn.net/ChengKing/archive/2009/01/01/3678774.aspx
    第4章  庖丁解牛系列—服务器控件属性 本章内容4.1  控件属性的作用4.2  简单属性4.3  属性的设计时特性4.4  复杂属性(这里包括你需要的: 4.4.2.1  连字符形式的复杂属性标记)4.5  深入研究——定制自己的属性编辑器4.6  类型转换器4.7  实现自定义属性