18.6 集合属性
本节将通过创建一个MyEmployeeCollection控件来演示集合属性的创建与使用。其中,MyEmployeeCollection控件将在页面中实现集合属性的持久性,它公开一个包含Employee对象的Employees集合属性,而Employees集合属性的Employee项将持久保存在该控件的标记中。如下面的示例所示:
<cc1:MyEmployeeCollection ID="MyEmployeeCollection1"
runat="server"BorderStyle="Solid"BorderWidth="1px">
<cc1:Employee Name="马伟"Email="madengwei@hotmail.com"
Phone="029-87111111"/>
<cc1:Employee Name="张军"Email="zhangjun@hotmail.com"
Phone="029-87222222"/>
<cc1:Employee Name="李海"Email="lihai@hotmail.com"
Phone="029-87333333"/>
</cc1:MyEmployeeCollection>
现在来首先创建一个MyEmployeeCollection类。如下面的代码所示:
[AspNetHostingPermission(SecurityAction.Demand,
Level=AspNetHostingPermissionLevel.Minimal),
AspNetHostingPermission(SecurityAction.InheritanceDemand,
Level=AspNetHostingPermissionLevel.Minimal),
DefaultProperty("Employees"),
ParseChildren(true,"Employees"),
ToolboxData("<{0}:MyEmployeeCollection runat=\"server\">
</{0}:MyEmployeeCollection>")]
public class MyEmployeeCollection:WebControl
{
private ArrayList employeesList;
[Category("Behavior"),
Description("The Employees collection"),
DesignerSerializationVisibility(
DesignerSerializationVisibility.Content),
Editor(typeof(ContactCollectionEditor),
typeof(UITypeEditor)),
PersistenceMode(PersistenceMode.InnerDefaultProperty)]
public ArrayList Employees
{
get
{
if(employeesList==null)
{
employeesList=new ArrayList();
}
return employeesList;
}
}
protected override void RenderContents(
HtmlTextWriter writer)
{
Table t=CreateContactsTable();
if(t!=null)
{
t.RenderControl(writer);
}
}
private Table CreateContactsTable()
{
Table t=null;
if(employeesList!=null&&employeesList.Count>0)
{
t=new Table();
foreach(Employee item in employeesList)
{
Employee aContact=item as Employee;
if(aContact!=null)
{
TableRow r=new TableRow();
TableCell c1=new TableCell();
c1.Text=aContact.Name;
r.Controls.Add(c1);
TableCell c2=new TableCell();
c2.Text=aContact.Email;
r.Controls.Add(c2);
TableCell c3=new TableCell();
c3.Text=aContact.Phone;
r.Controls.Add(c3);
t.Controls.Add(r);
}
}
}
return t;
}
}
如上面的代码所示,为了能够分析控件标记中的集合项,在MyEmployeeCollection控件中添加了ParseChildren(true,“Employees”)特性。ParseChildrenAttribute特性的第一个参数((tue)指定页分析器应将控件标记内的嵌套内容解释为属性,而不是子控件。第二个参数(“Employees”)提供了内部默认属性的名称。指定第二个参数时,控件标记中的内容必须与默认的内部属性((poperty, Employee对象)对应,而不与其他任何内容对应。
除此之外,MyEmployeeCollection控件还包括以下设计时特性,为实现设计时系列化和持久性,必须将这些特性应用于集合属性:
1)DesignerSerializationVisibilityAttribute:通过设置Content参数,可以指定可视化设计器应对属性的内容进行序列化。在该示例中,此属性包含Employee对象。
2)PersistenceModeAttribute:通过传递InnerDefaultProperty参数,可以指定可视化设计器应将属性保存为作为内部默认属性应用的特性。这表示可视化设计器会将属性保存在控件的标记中。特性只能应用于一个属性,因为其对应的控件标记中只能保存一个属性。属性值不会用特殊标记包装。
3)EditorAttribute:通过设置EditorAttribute特性来将集合编辑器与Employees集合属性关联,即
Editor(typeof(ContactCollectionEditor),typeof(UITypeEditor)),
通过将集合编辑器与属性关联,可以使可视化设计器中的属性浏览器打开集合编辑器以添加Employee项。这与用于编辑DropDownList控件或ListBox控件的Items属性的用户界面类似。其中,MyEmployeeCollection所使用的自定义集合编辑器ContactCollectionEditor将在本节的最后进行阐述。
为了清楚起见,MyEmployeeCollection控件不会定义强类型集合,而是使用ArrayList作为其集合类型。一般情况下,应该使用强类型集合作为集合属性的类型,这样应用程序开发人员就无法在集合中任意添加类型了。
下面来看如何设计Employee类。
其实,Employee类的设计很简单,就只是创建Name、Email与Phone三个属性而已。这里需要说明的是,与Employee类关联的ExpandableObjectConverter类型转换器使集合编辑器可以提供一个用于编辑子属性((Nme、Email、Phone)的展开/折叠的用户界面,这一点在上面一节也做了详细阐述。而应用于Name、Email和Phone属性的NotifyParentPropertyAttribute(其中构造函数参数等于true)会导致编辑器将这些属性中的更改序列化到其父属性(即Employee类的一个实例)。
Employee类的详细代码如下所示:
[TypeConverter(typeof(ExpandableObjectConverter))]
public class Employee
{
private string nameValue;
private string emailValue;
private string phoneValue;
public Employee()
:this(String.Empty, String.Empty, String.Empty)
{
}
public Employee(string name, string email, string phone)
{
nameValue=name;
emailValue=email;
phoneValue=phone;
}
[Category("Behavior"),
DefaultValue(""),
Description("Name of Employee"),
NotifyParentProperty(true)]
public String Name
{
get
{
return nameValue;
}
set
{
nameValue=value;
}
}
[Category("Behavior"),
DefaultValue(""),
Description("Email address of Employee"),
NotifyParentProperty(true)]
public String Email
{
get
{
return emailValue;
}
set
{
emailValue=value;
}
}
[Category("Behavior"),
DefaultValue(""),
Description("Phone number of Employee"),
NotifyParentProperty(true)]
public String Phone
{
get
{
return phoneValue;
}
set
{
phoneValue=value;
}
}
}
创建完MyEmployeeCollection与Employee类之后,还需要创建一个用于实现自定义集合编辑器的控件ContactCollectionEditor。它使得类型为Employee的对象可以通过集合编辑器用户界面添加至Employees属性中。
其中,ContactCollectionEditor类派生自CollectionEditor类,并重写了CreateCollectionItemType方法,以返回Employee类型。但是,如果控件的集合属性包含不同类型的对象,则需要重写CreateNewItemTypes方法,而不是CreateCollectionItemType方法,并返回正确的项类型。ContactCollectionEditor类如下面的代码所示:
public class ContactCollectionEditor:CollectionEditor
{
public ContactCollectionEditor(Type type)
:base(type)
{
}
protected override bool CanSelectMultipleInstances()
{
return false;
}
protected override Type CreateCollectionItemType()
{
return typeof(Employee);
}
}
到目前为止,一个完整的MyEmployeeCollection控件就创建完成了。现在,就可以在页面里使用该控件了。使用示例如下面的代码所示:
<form id="form1"runat="server">
<div>
<cc1:MyEmployeeCollection ID="MyEmployeeCollection1"
runat="server"BorderStyle="Solid"
BorderWidth="1px">
<cc1:Employee Name="马伟"Email="madengwei@hotmail.com"
Phone="029-87111111"/>
<cc1:Employee Name="张军"Email="zhangjun@hotmail.com"
Phone="029-87222222"/>
<cc1:Employee Name="李海"Email="lihai@hotmail.com"
Phone="029-87333333"/>
</cc1:MyEmployeeCollection>
</div>
</form>
示例代码运行结果如图18-8所示。
图 18-8 MyEmployeeCollection控件的示例运行结果