you need to write a CollectionEditor, see the HotSpotCollectionEditor example in Developing Microsoft ASP.NET Server Controls and Components 
by Nikhil Kothari and Vandana Datyeyou download the code here
http://www.microsoft.com/mspress/books/companion/5728.asp#Companion%20Contentor look into 
eCoolWebPanelBar - Outlook style Web Panel Bar
http://www.codeproject.com/aspnet/ecoolwebpanelbar.asp

解决方案 »

  1.   

    我有那本书,反复看了相关章节,但还是有些不明白的,书上主要说的是ASPX中已有子控件代码时的解析。但对如何加入子控件只是提了一下,说得不详细,始终未得要领。
      

  2.   

    另一本关于控件的书是wrox的,但在wrox网站上竟然找不到此书的源码,谁能告诉在哪下载?
      

  3.   

    这是属性设计时序列化的问题.
    学习ListControl是怎样做的就好了:public MyCtrl:WebControl
    {[DesignTimeSerializeVisibility(DesignTimeSerializeVisibility.Content)]
    [PersistenceMode(PersistenceMode.InnerProperty)]
    public ListItemCollection MyItems
    {
        get
        {
            return _items;
        }
    }
    ListItemCollection _items;}上面的Attribute名可能写错了。
    不过序列化子属性就用这两个就够了。顺便提提,如果Property类型是Control,那么vs.net不会把它们序列化.
    我的解决方法是用代码生成Control的一个代理类来处理子属性.可以参考
    http://www.lostinet.com/en/lwsamplecs/?framesrc=combocalendar/
    中的CalendarFacade
    下载
    http://www.lostinet.com/en/lwsamplecs.zip (临时,过了这段时间不下就没有机会咯~~)
      

  4.   

    这里帖出生成Control的Facade类的代码:
    namespace Sample
    {
    using System;
    using System.Collections;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Web;
    using System.Web.SessionState;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using System.Web.UI.HtmlControls; public class FacadeCodeAspx : System.Web.UI.Page
    {
    protected System.Web.UI.WebControls.TextBox TextBox1;

    private void Page_Load(object sender, System.EventArgs e)
    {
    // 在此处放置用户代码以初始化页面
    } #region Web 窗体设计器生成的代码
    override protected void OnInit(EventArgs e)
    {
    //
    // CODEGEN: 该调用是 ASP.NET Web 窗体设计器所必需的。
    //
    InitializeComponent();
    base.OnInit(e);
    }

    /// <summary>
    /// 设计器支持所需的方法 - 不要使用代码编辑器修改
    /// 此方法的内容。
    /// </summary>
    private void InitializeComponent()
    {    
    this.Load += new System.EventHandler(this.Page_Load); }
    #endregion
    protected override void OnLoad(EventArgs e)
    {
    base.OnLoad(e); TextBox1.Text=new FacadeCodeMaker(typeof(Calendar)).ToString();
    } }
    }
    namespace Sample
    {
    using System;
    using System.Collections;
    using System.ComponentModel;
    using System.Text;
    using System.IO;
    using System.Reflection;
    using System.Web;
    using System.Web.UI; public class FacadeCodeMaker
    {
    Type type;
    PropertyDescriptorCollection pdc;
    StringBuilder sb=new StringBuilder();
    System.CodeDom.Compiler.IndentedTextWriter w;
    string classname;
    public FacadeCodeMaker(Type t)
    {
    type=t;
    pdc=TypeDescriptor.GetProperties(type);
    w=new System.CodeDom.Compiler.IndentedTextWriter(new StringWriter(sb));
    classname=type.Name+"Facade"; GenCode();
    }
    void GenCode()
    {
    w.WriteLine("[System.ComponentModel.ToolboxItem(false)]");
    w.WriteLine("public class "+classname+" : System.ComponentModel.Component {");
    w.Indent++; GenAttributeClasses();

    w.WriteLine(type.FullName+" inst;");
    w.WriteLine("public "+type.FullName+" Get"+type.Name+"()");
    w.WriteLine("{");
    w.Indent++;
    w.WriteLine("return inst;");
    w.Indent--;
    w.WriteLine("}");
    w.WriteLine("public "+classname+"("+type.FullName+" "+type.Name+")");
    w.WriteLine("{");
    w.Indent++;
    w.WriteLine("inst="+type.Name+";");
    w.Indent--;
    w.WriteLine("}");
    w.WriteLine("override public string ToString(){return string.Empty;}"); foreach(PropertyDescriptor pd in pdc)
    {
    if(!pd.IsBrowsable)
    continue;
    if(pd.SerializationVisibility==DesignerSerializationVisibility.Hidden)
    continue;
    if(pd.PropertyType==typeof(object))
    continue; if(pd.Name=="ID")
    continue; GenProperty(pd);
    } w.Indent--;
    w.WriteLine("}");
    }
    void GenAttributeClasses()
    {
    w.WriteLine("[System.AttributeUsage(System.AttributeTargets.Property)]");
    w.WriteLine("public class CategoryAttribute:System.ComponentModel.CategoryAttribute{");
    w.Indent++;

    w.WriteLine("public CategoryAttribute(string propertyName):base(GetValue(propertyName)){}");
    w.WriteLine("static string GetValue(string propertyName){");
    w.Indent++; w.WriteLine("return System.ComponentModel.TypeDescriptor.GetProperties(typeof("+type.FullName+"))[propertyName].Category;"); w.Indent--;
    w.WriteLine("}"); w.Indent--;
    w.WriteLine("}");
    w.WriteLine("[System.AttributeUsage(System.AttributeTargets.Property)]");
    w.WriteLine("public class DescriptionAttribute:System.ComponentModel.DescriptionAttribute{");
    w.Indent++; w.WriteLine("public DescriptionAttribute(string propertyName):base(GetValue(propertyName)){}");
    w.WriteLine("static string GetValue(string propertyName){");
    w.Indent++; w.WriteLine("return System.ComponentModel.TypeDescriptor.GetProperties(typeof("+type.FullName+"))[propertyName].Description;"); w.Indent--;
    w.WriteLine("}"); w.Indent--;
    w.WriteLine("}");
    }
    void GenProperty(PropertyDescriptor pd)
    {
    foreach(Attribute attr in pd.Attributes)
    {
    if(attr is DefaultValueAttribute)
    {
    #region DefaultValueAttribute
    DefaultValueAttribute a=(DefaultValueAttribute)attr;
    object v=a.Value;
    string cstr=null;
    if(v==null)
    {
    cstr="null";
    }
    else if(v is String)
    {
    cstr="@\""+v.ToString().Replace("\"","\"\"")+"\"";
    }
    else if(v is bool)
    {
    cstr=v.Equals(true)?"true":"false";
    }
    else if(v is Int16||v is Int32||v is Int64)
    {
    cstr=v.ToString();
    }
    else if(v is Single)
    {
    cstr=v.ToString()+"f";
    }
    else if(v is Double)
    {
    cstr=v.ToString()+"d";
    }
    else
    {
    cstr="typeof("+v.GetType().FullName+"),@\""+TypeDescriptor.GetConverter(v).ConvertToString(v).ToString().Replace("\"","\"\"")+"\"";
    }
    w.WriteLine("[System.ComponentModel.DefaultValue("+cstr+")]");
    #endregion
    }
    if(attr is PersistenceModeAttribute)
    {
    PersistenceModeAttribute a=(PersistenceModeAttribute)attr;
    w.WriteLine("[System.Web.UI.PersistenceMode(PersistenceMode."+a.Mode.ToString()+")]");
    }
    }

    w.WriteLine("[Category(\""+pd.Name+"\")]");
    w.WriteLine("[Description(\""+pd.Name+"\")]");
    w.WriteLine("[System.ComponentModel.NotifyParentProperty(true)]");
    w.WriteLine("[System.ComponentModel.DesignerSerializationVisibility(System.ComponentModel.DesignerSerializationVisibility."+pd.SerializationVisibility+")]");
    w.WriteLine("public "+pd.PropertyType.FullName+" "+pd.Name);
    w.WriteLine("{");
    w.Indent++;

    w.WriteLine("get");
    w.WriteLine("{");
    w.Indent++;
    w.WriteLine("return inst."+pd.Name+";");
    w.Indent--;
    w.WriteLine("}"); if(!pd.IsReadOnly)
    {
    w.WriteLine("set");
    w.WriteLine("{");
    w.Indent++;
    w.WriteLine("inst."+pd.Name+"=value;");
    w.Indent--;
    w.WriteLine("}");
    } w.Indent--;
    w.WriteLine("}");
    } public override string ToString()
    {
    return sb.ToString();
    } }
    }
      

  5.   

    多谢各位大侠,ListControl的例子,我昨晚在其它贴上看到了,并试了一下,确实可以序列化子控件。但它的子控件为组件,后来我将它换成webcontrol,没有成功,所以就发了这个贴。今晚再仔细研究研究。
      

  6.   

    终于搞定了,这个例子对我帮助很大:http://www.codeproject.com/aspnet/ecoolwebpanelbar.asp
    虽然它在我机上运行不了。:D
    下面我再将我的经验总结下,以免同仁走弯路。
    1。将子控件单独定义为类,如public class noteItem{},为了不让此子控件在整个DLL被拖入工具箱时不显示,可以使用此特性:[ToolboxItem(false)]
    2.子控件应有集合类,引用System.Collection ,定义:
    public class noteItemCollection:Ilist
    3.实现IList接口成员:
    CopyTo(),Count,IsSynchronized,SyncRoot,GetEnumerator,Add(),Clear(),Contains,IndexOf,Insert,IsFixedSize,IsReadOnly,Remove,RemoveAty,this[int]。
    4.在主类中定义加入特性:
    [DesignerSerializationvisibility(DesignerSerializationvisibility.Content)]
    [PersistenceMode(PersistenceMode.InnerDefaultProperty)]
    [Editor(typeof(CollectionEditor),typeof(UITypeEditor))]
    如果实现了自己的编辑器,可将typeof(CollectionEditor)换成自己的,可以是WinForm的,可参考TreeView源码。
    5.在主类中定义一集合变量和一ArrayList变量,如:
    private noteItemCollection _notes;
    private ArrayList al;
    在构造函数内写:
    {
    al = new ArrayList();
    _notes = new noteItemCollection(al);
    }后天散分。
      

  7.   

    像你那样实现IList很麻烦的.
    实际上一个类型化的Collection继承System.Collections.CollectionBase就是
    ItemType this[int index]
    Add(ItemType)
    是必须的.
      

  8.   

    多谢LevinForum指点,确实,ILIST接口的大部分成员我都是直接拷贝而没有修改的,应该是可从CollectionBase继承过来。
    另问一点:Remove()方法不是必须的吗?
      

  9.   

    缺少[ParseChildren(bool, string)]
      

  10.   

    是否需要实现ControlCollection,不知道楼主考虑过没有??
      

  11.   

    To: xiandaliu
    我定义的noteItemCollection就是一个控件集合呀。
    我试过,不加此特性是可以的,[ParseChildren(bool, string)]
    可能它默认就是[ParseChildren(false)]吧。