在上一篇文章中,曾经提及“复杂属性”的概念。复杂属性的最大特征是属性的类型是本身具有属性(称为子属性)的类。通常情况下,复杂属性表现为3种形式:连字符形式属性、内部嵌套形式属性和内部嵌套形式默认属性。本文将介绍以上3种形式复杂属性的具体实现方法。
1. 实现连字符形式复杂属性
连字符形式属性是比较常见的复杂属性。我们常用的Font属性就是一个复杂属性,其包括多个子属性,如Bold、Name等。这种类型属性具有两种语法格式:一种是利用连字符语法,在控件的开始标记中保存子属性,例如,Font-Bold,Font-Name。另外一种格式是在控件的标记中保存子属性,例如,<font Bold="true" />。后者比前者的可读性强。
实现连字符形式的复杂属性,必须对该复杂属性及其子属性实现设置指定的设计时元数据。下面首先列举了复杂属性实现过程中的相关元数据设置示例。请阅读下面的源代码。
public class CustomerControl:WebControl{
[ DesignerSerializationVisibility( DesignerSerializationVisibility.Content), NotifyParentProperty(true) ]
public SizeInfo Size { ...... }
}
如上代码所示,Size是一个复杂属性,其属性类型为SizeInfo(自定义类)。在Size属性实现前设置了两个设计时元数据:DesignerSerializationVisibility和NotifyParentProperty。DesignerSerializationVisibility用于指定在设计时序列化组件上的属性时,所使用的持久性类型。其值设置为DesignerSerializationVisibility.Content枚举值,用于指定序列化程序应该序列化属性的内容即子属性,而不是Size属性本身,因为序列化Size没有任何意义。另外,还包括一个NotifyParentProperty(true)设置,它的作用是使得属性浏览器中对子属性的修改通知一直上传到对象模型,并在被修改了子属性的控件中产生修改通知。
在完成了复杂属性的元数据设置后,开发人员还必须对子属性设置相关的设计时元数据。例如,Size包括两个子属性Height和Width,那么它们的实现代码应如下所示。
[TypeConverter(typeof(ExpandableObjectConverter))]public class SizeInfo{
[ NotifyParentProperty(true) ]
public UInt32 Height {......}
[ NotifyParentProperty(true) ]
public UInt32 Width {......}
}
如上代码所示,子属性Height和Width分别被设置了元数据NotifyParentProperty(true)。这样,当子属性发生修改时,.NET框架将自动产生修改通知,并且通知到父属性Size。另外,还有一个设计时特性TypeConverter(typeof(ExpandableObjectConverter)),它告诉属性浏览器提供扩展和折叠样式,这样控件开发者可以在属性浏览器中直接编辑子属性。
以上介绍了声明连字符形式属性的方法。从中可以看出,在声明连字符属性过程中需要把握两个要点:一是复杂属性的设计时元数据设置;二是子属性的设计时特性设置。
2. 实现内部嵌套形式复杂属性
通常情况下,开发人员多实现连字符形式的复杂属性。然而,对复杂属性还可以实现内部嵌套的形式。下面的代码就是一个典型的内部潜逃形式复杂属性的应用。
<MyControl:CustomeControl id="demo1" runat="server">
<HeaderStyle ForeColor="#FFFF00" BackColor="#99ff00">
</HeaderStyle>
... ...
</MyControl: CustomeControl>
如上代码所示,自定义控件MyControl的属性HeaderStyle是一个典型的内部嵌套形式属性。实现这种形式的属性与实现连字符形式属性有很大不同,需要分为两种情况。
如果自定义服务器控件类继承自Control类,那么必须在控件类之前设置元数据属性ParseChildren和PersistChildren。示意性代码如下所示。
[ParseChildren(true),PersistChildren(false)]
public class CustomeControl:Control{ ......}
如上代码所示,在控件类前设置了两个元数据属性ParseChildren和PersistChildren。前者用于告知页面分析器把控件标记中的内容解析为属性还是子控件,该属性值设置为true,则表示解析为属性。后者用于告知设计器把控件标记中的内容保存为属性还是子控件,该属性值设置为false,表示保存为属性。
如果自定义控件类继承自WebControl类,那么就不需要以上的元数据属性设置,因为,WebControl类已经应用了这些元数据属性了。
无论自定义控件类继承自WebControl类还是Control类,为实现内部嵌套形式复杂属性,都必须在属性实现中设置如下元数据属性。
[ DesignerSerializationVisibility( DesignerSerializationVisibility.Content), NotifyParentProperty(true), PersistenceMode(PersistenceMode.InnerProperty)]
public TableItemStyle HeaderStyle{......}
由上面的代码可以看到,必须在实现复杂属性之前,应用3个元数据属性:DesignerSerializationVisibility、NotifyParentProperty和PersistenceMode。前两个元数据属性在前文中已经说明,第三个PersistenceMode用于指定如何将服务器控件属性或事件保持到ASP.NET页的元数据属性,该特性的值设置为枚举值:PersistenceMode.InnerProperty,这表示将所标识属性(HeaderStyle)保持为嵌套标记。
以上介绍了内部嵌套形式属性声明的方法。总结起来分为两种情况:一是所开发控件从Control派生,则需要设置五个设计时特性ParseChildrenAttribute(true)、PersistChildren(false)、DesignerSerializationVisibility、NotifyParentProperty和PersistenceMode。前两个特性在控件类前设置,用于告诉编译器将控件标记内的内容为属性,需要解析为属性;后三个特性在属性前指定,用于指示编译器此属性为内部嵌套形式属性,在应用控件属性时,必须采用嵌套形式。二是所开发控件从WebControl派生,这种情况比较简单,只需设置上文中后3个设计时特性即可。
3. 实现内部嵌套形式默认复杂属性
内部嵌套形式默认属性与内部嵌套形式属性非常类似,它通常用于设置某个控件的集合属性。例如,标准服务器控件中的DataList、DropDownList控件中的属性均为内部嵌套形式默认属性。
为了实现这种形式的属性,主要需设置两个元数据属性:一是在控件类前设置ParseChildren(true, "DefaultPropertyName"),指定该控件中嵌套的标记表示属性,而非子控件,同时将嵌套属性分析为该控件的集合属性;二是在集合属性前设置特性PersistenceMode(PersistenceMode.InnerDefaultProperty),表示将该属性定义为控件的默认属性。
4. 小结
本文介绍了创建复杂属性的实现方法。这是实现自定义服务器控件过程中的重点和难点内容。在随后的一篇文章中,我们将通过示例来加深对复杂属性实现方法的认识。