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
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
学习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 (临时,过了这段时间不下就没有机会咯~~)
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();
} }
}
虽然它在我机上运行不了。: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);
}后天散分。
实际上一个类型化的Collection继承System.Collections.CollectionBase就是
ItemType this[int index]
Add(ItemType)
是必须的.
另问一点:Remove()方法不是必须的吗?
我定义的noteItemCollection就是一个控件集合呀。
我试过,不加此特性是可以的,[ParseChildren(bool, string)]
可能它默认就是[ParseChildren(false)]吧。