asp.net|服务器|控件|控件开发
在前面的系列文章中,笔者已经列举了几个实现自定义服务器控件的示例。通过这些示例,读者初步接触了有关创建服务器控件属性的内容。例如,使用私有变量、视图状态、控件状态等实现属性等等。虽然读者通过这些内容可以了解实现属性的一些基本知识,但是这还是不够的。从本节开始,将针对实现自定义服务器控件属性的问题展开讲解。本节重点介绍实现自定义服务器控件属性的一些基本概念和简单属性的基本实现方法等内容。
1. 控件属性基本概念
本小节介绍有关创建服务器控件属性的基本内容,具体内容包括:(1)属性类型和形式;(2)从Control和WebControl继承的属性;(3)与属性相关的设计时元数据attribute。
1) 属性类型和形式
通常情况下,服务器控件属性可以分为两种类型:简单属性和复杂属性。
简单属性是指属性值可以很容易转换为字符串表达式的属性,这种属性的值通常为Boolean、Byte、Char、Double、Enum、Int32、DateTime等简单数值类型,以及String类型和枚举类型。开发人员可以通过添加代码,将简单属性存储在ViewState字典中,以在回发间进行状态管理。如果一个属性的类型是本身具有属性(称为子属性)的类,则该属性就称为复杂属性。例如,WebControl类的Font属性的类型是本身具有属性(如Bold和Name)的FontInfo类。Bold和Name是WebControl的Font属性的子属性。ASP.NET页框架可通过使用带有连字符的语法(例如Font-Bold="true")在控件的开始标记上保存子属性,但如果在控件的标记(例如<font Bold="true">)中保存子属性,则子属性在页中的可读性更强。
在上文中谈到了属性的标记形式,即添加连字符的形式。实际上,不同的属性表现出不同的标记形式。为了加深对简单属性和复杂属性的认识,下面介绍一下有关属性的4种标记形式。
· 通用形式属性
这是一种最为常见的属性标记形式。这种形式的属性标记位于控件内部,与runat="server"一起定义。通常为以下形式:
<MyControl:CustomerControl id="demo1" runat="server" PropertyName="PropertyValue"/>
其中PropertyName为一个不带连字符的单词。例如:
<asp:Button id="button1" runat="server" Text="Submit"/>
此处的属性Text属于通用形式属性。
· 连字符形式属性
这种标记形式的属性位于控件标记内部,带有连字符是这种形式属性的最大特征。其形式为:
<MyControl:CustomerControl id="demo1" runat="server" Sub-PropertyName="PropertyValue"/>
其中Sub-PropertyName为一个带连字符的单词组合。例如:
<asp:Label id="label1" runat="server" Font-Size="Medium" Font-Underline="True" />
在上面的代码中,Font-Size和Font-Underline就是典型的连字符形式属性。
· 内部嵌套形式属性
凡是具有这种标记形式的属性均为复杂属性。它是以嵌套形式在控件标记内部声明某属性集的子属性。其形式类似:
<asp:DataGrid id="DataGrid1" runat="server">
<HeaderStyle ForeColor="#FFFFCC" BackColor="#990000">
</HeaderStyle>
<FooterStyle ForeColor="#330099" BackColor="#FFFFCC">
</FooterStyle>
</asp:DataGrid>
其中HeaderStyle是内部嵌套形式属性,ForeColor和BackColor是HeaderStyle属性的子属性。FooterStyle与HeaderStyle是一样的,也是内部嵌套形式属性。
· 内部嵌套形式默认属性
这种标记形式的属性通常用于服务器控件的集合属性,具有这种形式的属性必然是复杂属性。该形式属性与上文所述"内部嵌套形式属性"的标记形式基本相同。不同之处在于:当某控件具有这种属性时,控件标记中只包含该形式属性,不能包含其他任何属性。这就是为什么称为"默认"的原因。其形式类似:
<asp:DropDownList id="DropDownList1" runat="server">
<asp:ListItem>1</asp:ListItem>
<asp:ListItem>2</asp:ListItem>
<asp:ListItem>3</asp:ListItem>
<asp:ListItem>4</asp:ListItem>
</asp:DropDownList>
其中属性ListItem就是典型的内部嵌套形式默认属性。
2) 从Control和WebControl继承的属性
如前面文章所述,如果需要开发没有UI的控件或者组合其他呈现它们自己的UI的控件,则从System.Web.UI.Control基类派生。为此,读者应该了解一些Control类的常见属性。如表1列举了Control基类常用属性,它们在开发服务器控件过程中经常被使用。
属性 | 数据类型 | 说明 |
Controls | ControlCollection | 获取 ControlCollection 对象,该对象表示 UI 层次结构中指定服务器控件的子控件 |
Adapter | ControlAdapter | 获取控件的浏览器特定适配器。(asp.net 2.0新增) |
AppRelativeTemplateSourceDirectory | string | 获取或设置包含该控件的 Page 或 UserControl 对象的应用程序相对虚拟目录。(asp.net 2.0新增) |
EnableTheming | bool | 获取或设置一个值,该值指示是否对此控件应用主题。(asp.net 2.0新增) |
Page | Page | 获取对包含服务器控件的 Page 实例的引用。 |
Parent | Control | 控件属于其Controls集合的控件。(如果控件B是A.Controls的一个元素,则控件A是控件B的父级) |
EnableViewState | Bool | 指示控件在往返过程中是否维护其视图状态。如果父控件不维护其视图状态,则自动不维护其子控件的视图状态 |
TemplateControl | TemplateControl | 获取或设置对包含该控件的模板的引用。(asp.net 2.0新增) |
UniqueID | String | 页框架给控件分配的分层限定的唯一标识符 |
ClientID | String | 给控件分配的唯一标识符,该唯一标识符在客户端上呈现为HTML ID特性。ClientID与UniqueID是不同的,这是因为UniqueID可以包含冒号字符(:),而在HTML ID特性中该字符无效(并且不允许在客户端脚本的变量名中使用) |
页框架
如前面文章所述,如果创建具有UI的自定义服务器控件,则应该从WebControl或System.Web.UI.WebControls中的任何控件派生,该命名空间为自定义控件提供适当的起点。同样的道理,读者应了解一些来自WebControl类的常见属性,它们可为控件自动继承。表2列举了这些属性。
属性 | 数据类型 | 说明 |
BackColor | Color | 获取或设置Web服务器控件的背景色。 |
BorderColor | Color | 获取或设置Web控件的边框颜色。 |
BorderStyle | BorderStyle | 获取或设置Web服务器控件的边框样式。 |
BorderWidth | Unit | 获取或设置Web服务器控件的边框宽度。 |
ControlStyle | Style | 获取Web服务器控件的样式。 |
CssClass | String | 获取或设置由Web服务器控件在客户端呈现的级联样式表(CSS)类。 |
Enabled | Bool | 获取或设置一个值,该值指示是否启用Web服务器控件。 |
EnableTheming | bool | 获取或设置一个值,该值指示是否对此控件应用主题。(asp.net 2.0新增) |
Font | FontInfo | 获取与Web服务器控件关联的字体属性。 |
ForeColor | Color | 获取或设置Web服务器控件的前景色(通常是文本颜色)。 |
Height | Unit | 服务器控件高度 |
Width | Unit | 服务器控件宽度 |
SkinID | string | 获取或设置要应用于控件的外观。(asp.net 2.0新增) |
3) 与属性相关的设计时元数据
创建服务器控件是为了提高应用开发效率,每个控件开发者都希望自己创建出的控件能够像.NET框架中的内置标准服务器控件那样功能强大且易于使用。例如,当控件应用者在设计界面点击控件时,可能会希望某些属性能够高亮显示,某些属性能够显示在属性浏览器中等等。如何才能使控件具有这样的功能呢?这就需要在代码中加入相关的设计时支持代码。
实际上,实现设计时元数据是一个比较复杂的内容。然而,作为初学者而言,我们没有必要掌握得过于深入,下面笔者只讲解一些常见的与属性相关的设计时元数据设置。如下所示代码,列举了一些与属性相关的设计时元数据设置和简要说明。
· Bindable
这个特性表示属性是否可以绑定一个有效数据源。通常使用布尔值进行设置,例如:Bindable(true)。如果使用值true标记属性,表示该属性可以绑定一个有效数据源,且应引发该属性的属性更改通知;如果属性值为false,则表示该属性不能绑定数据。
· Browsable
指定属性是否应该在属性浏览器中显示,使用布尔值设置。通常情况下,公用属性和那些希望在属性浏览器中显示的属性被设置为Browsable(true),只读属性和那些不希望在属性浏览器中见到的属性被设置为Browsable(false)。
· Category
指定属性在属性浏览器中进行分组显示的类别。该设计时特性帮助可视化编辑器将属性进行逻辑分组。通常分为:外观(Appearance)、行为(Behavior)、布局(Layout)、数据(Data)、操作(Action)、键盘(Key)、鼠标(Mouse)等。除此之外,读者还可以自定义分类,例如Category("ItemStyle"),表示该属性在属性浏览器中显示为ItemStyle一组。
· Description
指定显示在属性浏览器下方,属性的文字说明。例如:Description("this is a property")。
以上内容是实现属性过程中最为常见的设计时元数据设置。无论对于简单属性,还是复杂属性都应该根据需要设置。
· DesignerSerializationVisibility
指定属性是否以及如何在代码中序列化,其值为DesignerSerializationVisibility的枚举值。存在三种设置方式:
(1)DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),指定序列化程序不应该序列化属性的值;
(2)DesignerSerializationVisibility(DesignerSerializationVisibility.Visible),指定应该允许序列化程序序列化属性的值;
(3)DesignerSerializationVisibility(DesignerSerializationVisibility.Content),指定序列化程序应该序列化属性的内容,而不是属性本身。此字段为只读。需要注意的是:没有DesignerSerializationVisibility特性的成员将被视为具有值为DesignerSerializationVisibility.Visible的DesignerSerializationVisibility特性。如果可能,序列化程序会将标记为Visible的属性值序列化为该类型。
· NotifyParentProperty
指示当此特性应用到的属性的值被修改时将通知其父属性。换言之,如果属性的父属性应该在该属性值更改时接到通知,则向该属性应用NotifyParentProperty特性。通常使用布尔值进行设置。例如,Size属性具有两个嵌套的子属性:Width和Height。那么属性Width和Height就应标记为NotifyParentPropertyAttribute(true),以便当属性值更改时,它们可以通知父属性来更新其值并显示。
· ParseChildren
使用该特性指示当在页上以声明方式使用控件时,嵌套在服务器控件标记内的XML元素是应该视为属性还是应视为子控件。通常情况下,包含两种声明方式:(1)ParseChildren(true),表示将子XML元素作为服务器控件的属性分析,ParseChildren(false),表示将子XML元素作为服务器控件的子控件分析;(2)ParseChildren(bool childrenasProperty , string defaultProperty),其中childrenasPropety和方式1中的布尔值参数意义相同,defaultProperty定义默认情况下将子控件分析为的服务器控件的集合属性。
· PersistChildren
该特性指示设计时是否应将服务器控件的子控件作为内部嵌套控件保持。如果该特性为PersistChildren(true),则将服务器控件的子控件作为嵌套服务器控件标记保持。如果为PersistChildren(false),则将该控件的属性作为嵌套元素保持。
· PersistenceMode
指定如何将服务器控件属性或事件保持到ASP.NET页的元数据属性。共存在4种枚举设置方式:
(1)PersistenceMode(PersistenceMode.Attribute),指定属性或事件保持为特性;
(2)PersistenceMode(PersistenceMode.EncodedInnerDefaultProperty),指定属性作为服务器控件的唯一内部文本而。属性值是HTML编码的。只能对字符串做这种指定;
(3)PersistenceMode(PersistenceMode.InnerDefaultProperty),指定属性在服务器控件中保持为内部文本。还指示将该属性定义为元素的默认属性。只能指定一个属性为默认属性;
(4)PersistenceMode(PersistenceMode.InnerProperty),指定属性在服务器控件中保持为嵌套标记。这通常用于复杂对象;它们具有自己的持久性属性;
· DefaultProperty
指定服务器控件的默认属性。例如:[DefaultProperty("MyProperty")]。
· TypeConverter
指定用作此特性所绑定到的对象的转换器的类型。用于转换的类必须从TypeConverter继承。使用ConverterTypeName属性来获取为该特性所绑定到的对象提供数据转换的类名。
2. 简单属性实现方法
在前面的几篇文章中已经介绍了一些简单属性的实现方法。从中可以发现创建简单属性可以使用私有变量、视图状态和控件状态等。在此,笔者无意对这些内容进行重复。感兴趣的读者可参阅有关文章。本节仅对实现简单属性的过程进行总结,并通过一个实现简单枚举属性的示例加以说明。示例代码如下所示:
// 定义枚举
public enum BookType{
NotDefined = 0, Fiction = 1, NonFiction = 2
}
// 实现属性BookType[Bindable(true),Category("Appearance"),DefaultValue(BookType.NotDefined),Description("Fiction or Not"),]
public virtual BookType BookType{
get {
object t = ViewState["BookType"];
return (t == null) ? BookType.NotDefined : (BookType)t;
}
set { ViewState["BookType"] = value; }
}
以上代码实现了一个枚举BookType(包括3个枚举值)和一个类型为BookType的属性BookType。根据前文所述基本概念可知,BookType是一个简单属性。同时,该属性将属性值存储在视图状态ViewState中。通过这个实例,我们基本可以总结出简单属性的实现方法:
(1)判断所要声明的属性是否是通用形式属性;
(2)判断所要声明的属性所封装的属性值是否是简单数值类型、String还是枚举类型等;
(3)如果步骤1和2都为真,则判定所要声明的属性是简单属性;
(4)声明该属性的设计时特性;
(5)根据属性的设计需求,编写读写访问器代码;
3. 小结
本文介绍了利用ASP.NET 2.0技术,为自定义服务器控件创建简单属性的内容。随着读者对自定义服务器控件开发的逐步理解将会发现,实现简单属性是构建控件过程中较为简单,也是较为常见的实现内容。在创建过程中,读者必须了解使用私有变量、控件状态和视图状态的不同之处。这样才能又快又好的实现简单属性。