Web 控件集合属性示例
更新:2007 年 11 月
此示例演示如何创建一个名为 QuickContacts 的控件,该控件将在页面中实现集合属性的持久性。该示例控件是一个允许页开发人员存储通讯簿联系人列表的控件。QuickContacts 控件会公开一个包含 Contact 对象的 Contacts 集合属性。Contact 类具有 Name、Email 和 Phone 属性。
Contacts 集合属性的 Contact 项持久保存在该控件的标记中,如下面的示例所示:
<aspSample:QuickContacts ID="QuickContacts1" Runat="server">
<aspSample:Contact Name="someone" Email="someone@example.com" Phone="(555) 555-5555"/><aspSample:Contact Name="jae" Email="jae@fourthcoffee.com" Phone="(555) 555-5555"/>
</aspSample:QuickContacts>
为清楚起见,QuickContacts 控件不会实现集合属性的状态管理。假设集合项以声明方式添加到页中,或者如果已在代码中创建,则必须在回发时重新创建。在高质量的控件中,将实现状态管理。有关详细信息,请参见服务器控件自定义状态管理。
QuickContacts 控件的代码清单
' QuickContacts.vb
Option Strict On
Imports System
Imports System.ComponentModel
Imports System.Collections
Imports System.Drawing.Design
Imports System.Security.Permissions
Imports System.Web
Imports System.Web.UI
Imports System.Web.UI.WebControls
Namespace Samples.AspNet.VB.Controls
< _
AspNetHostingPermission(SecurityAction.Demand, _
Level:=AspNetHostingPermissionLevel.Minimal), _
AspNetHostingPermission(SecurityAction.InheritanceDemand, _
Level:=AspNetHostingPermissionLevel.Minimal), _
DefaultProperty("Contacts"), _
ParseChildren(True, "Contacts"), _
ToolboxData( _
"<{0}:QuickContacts runat=""server""> </{0}:QuickContacts>") _
> _
Public Class QuickContacts
Inherits WebControl
Private contactsList As ArrayList
< _
Category("Behavior"), _
Description("The contacts collection"), _
DesignerSerializationVisibility( _
DesignerSerializationVisibility.Content), _
Editor(GetType(ContactCollectionEditor), _
GetType(UITypeEditor)), _
PersistenceMode(PersistenceMode.InnerDefaultProperty) _
> _
Public ReadOnly Property Contacts() As ArrayList
Get
If contactsList Is Nothing Then
contactsList = New ArrayList
End If
Return contactsList
End Get
End Property
' The contacts are rendered in an HTML table.
Protected Overrides Sub RenderContents( _
ByVal writer As HtmlTextWriter)
Dim t As Table = CreateContactsTable()
If t IsNot Nothing Then
t.RenderControl(writer)
End If
End Sub
Private Function CreateContactsTable() As Table
Dim t As Table = Nothing
If (contactsList IsNot Nothing) AndAlso _
(contactsList.Count > 0) Then
t = New Table
For Each item As Contact In contactsList
Dim aContact As Contact = TryCast(item, Contact)
If aContact IsNot Nothing Then
Dim r As New TableRow
Dim c1 As New TableCell
c1.Text = aContact.Name
r.Controls.Add(c1)
Dim c2 As New TableCell
c2.Text = aContact.Email
r.Controls.Add(c2)
Dim c3 As New TableCell
c2.Text = aContact.Phone
r.Controls.Add(c3)
t.Controls.Add(r)
End If
Next
End If
Return t
End Function
End Class
End Namespace
// QuickContacts.cs
using System;
using System.ComponentModel;
using System.Collections;
using System.Drawing.Design;
using System.Security.Permissions;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace Samples.AspNet.CS.Controls
{
[
AspNetHostingPermission(SecurityAction.Demand,
Level = AspNetHostingPermissionLevel.Minimal),
AspNetHostingPermission(SecurityAction.InheritanceDemand,
Level=AspNetHostingPermissionLevel.Minimal),
DefaultProperty("Contacts"),
ParseChildren(true, "Contacts"),
ToolboxData(
"<{0}:QuickContacts runat=\"server\"> </{0}:QuickContacts>")
]
public class QuickContacts : WebControl
{
private ArrayList contactsList;
[
Category("Behavior"),
Description("The contacts collection"),
DesignerSerializationVisibility(
DesignerSerializationVisibility.Content),
Editor(typeof(ContactCollectionEditor), typeof(UITypeEditor)),
PersistenceMode(PersistenceMode.InnerDefaultProperty)
]
public ArrayList Contacts
{
get
{
if (contactsList == null)
{
contactsList = new ArrayList();
}
return contactsList;
}
}
// The contacts are rendered in an HTML table.
protected override void RenderContents(
HtmlTextWriter writer)
{
Table t = CreateContactsTable();
if (t != null)
{
t.RenderControl(writer);
}
}
private Table CreateContactsTable()
{
Table t = null;
if (contactsList != null && contactsList.Count > 0)
{
t = new Table();
foreach (Contact item in contactsList)
{
Contact aContact = item as Contact;
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;
}
}
}
代码讨论
为了能够分析控件标记中的集合项,QuickContacts 控件在其中添加了 ParseChildren(true, "Contacts") 属性 (Attribute)。ParseChildrenAttribute 的第一个参数 (true) 指定页分析器应将控件标记内的嵌套内容解释为属性 (Property),而不是子控件。第二个参数 ("Contacts") 提供了内部默认属性 (Property) 的名称。指定第二个参数时,控件标记中的内容必须与默认的内部属性(property,Contact 对象)对应,而不与其他任何内容对应。
QuickContacts 控件还包括以下设计时属性 (Attribute),为实现设计时系列化和持久性,必须将这些属性 (Attribute) 应用于集合属性 (Property):
DesignerSerializationVisibilityAttribute 通过设置 Content 参数,可以指定可视化设计器应对属性的内容进行序列化。在该示例中,此属性包含 Contact 对象。
PersistenceModeAttribute 通过传递 InnerDefaultProperty 参数,可以指定可视化设计器应将属性 (Property) 保存为作为内部默认属性 (Property) 应用的属性 (Attribute)。这表示可视化设计器会将属性 (Property) 保存在控件的标记中。属性 (Attribute) 只能应用于一个属性 (Property),因为其对应的控件标记中只能保存一个属性 (Property)。属性 (Property) 值不会用特殊标记包装。
QuickContacts 控件使用 EditorAttribute 将集合编辑器与 Contacts 集合属性关联,如下面的示例所示:
Editor(typeof(ContactCollectionEditor), typeof(UITypeEditor))
Editor(GetType(ContactCollectionEditor), GetType(UITypeEditor))
通过将集合编辑器与属性关联,可以使可视化设计器中的属性浏览器打开集合编辑器以添加 Contact 项。这与用于编辑 DropDownList 控件或 ListBox 控件的 Items 属性的用户界面 (UI) 类似。QuickContacts 所使用的自定义集合编辑器 ContactCollectionEditor 在集合编辑器示例中进行了描述。
为了清楚起见,QuickContacts 控件不会定义强类型集合,而是使用 ArrayList 作为其集合类型。一般情况下,应该使用强类型集合作为集合属性的类型,这样应用程序开发人员就无法在集合中任意添加类型了。
Contact 类的代码清单
编辑属性 (Property) 和进行设计时序列化时,需要使用 Contact 类代码中的设计时属性 (Attribute)。与 Contact 类关联的 ExpandableObjectConverter 类型转换器(使用 TypeConverterAttribute)使集合编辑器可以提供一个用于编辑子属性(Name、Email、Phone)的展开/折叠的用户界面。该界面类似于在可视化设计器的属性浏览器中编辑 Web 控件的 Font 属性时看到的用户界面。应用于 Name、Email 和 Phone 属性的 NotifyParentPropertyAttribute(其中构造函数参数等于 true)会导致编辑器将这些属性中的更改序列化到其父属性(即 Contact 类的一个实例)。
' Contact.vb
' The type of the items in the Contacts collection property
' in QuickContacts.
Option Strict On
Imports System
Imports System.Collections
Imports System.ComponentModel
Imports System.Web.UI
Namespace Samples.AspNet.VB.Controls
< _
TypeConverter(GetType(ExpandableObjectConverter)) _
> _
Public Class Contact
Private _name As String
Private _email As String
Private _phone As String
Public Sub New()
Me.New(String.Empty, String.Empty, String.Empty)
End Sub
Public Sub New(ByVal name As String, _
ByVal email As String, ByVal phone As String)
_name = name
_email = email
_phone = phone
End Sub
< _
Category("Behavior"), _
DefaultValue(""), _
Description("Name of contact"), _
NotifyParentProperty(True) _
> _
Public Property Name() As String
Get
Return _name
End Get
Set(ByVal value As String)
_name = value
End Set
End Property
< _
Category("Behavior"), _
DefaultValue(""), _
Description("Email address of contact"), _
NotifyParentProperty(True) _
> _
Public Property Email() As String
Get
Return _email
End Get
Set(ByVal value As String)
_email = value
End Set
End Property
< _
Category("Behavior"), _
DefaultValue(""), _
Description("Phone number of contact"), _
NotifyParentProperty(True) _
> _
Public Property Phone() As String
Get
Return _phone
End Get
Set(ByVal value As String)
_phone = value
End Set
End Property
End Class
End Namespace
// Contact.cs
// The type of the items in the Contacts collection property
//in QuickContacts.
using System;
using System.Collections;
using System.ComponentModel;
using System.Web.UI;
namespace Samples.AspNet.CS.Controls
{
[
TypeConverter(typeof(ExpandableObjectConverter))
]
public class Contact
{
private string nameValue;
private string emailValue;
private string phoneValue;
public Contact()
: this(String.Empty, String.Empty, String.Empty)
{
}
public Contact(string name, string email, string phone)
{
nameValue = name;
emailValue = email;
phoneValue = phone;
}
[
Category("Behavior"),
DefaultValue(""),
Description("Name of contact"),
NotifyParentProperty(true),
]
public String Name
{
get
{
return nameValue;
}
set
{
nameValue = value;
}
}
[
Category("Behavior"),
DefaultValue(""),
Description("Email address of contact"),
NotifyParentProperty(true)
]
public String Email
{
get
{
return emailValue;
}
set
{
emailValue = value;
}
}
[
Category("Behavior"),
DefaultValue(""),
Description("Phone number of contact"),
NotifyParentProperty(true)
]
public String Phone
{
get
{
return phoneValue;
}
set
{
phoneValue = value;
}
}
}
}
QuickContacts 控件的测试页
下面的示例演示一个使用 QuickContacts 控件的 .aspx 页。
<%@ Page Language="VB"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
<title>
QuickContacts test page
</title>
</head>
<body>
<form id="Form1" runat="server">
<aspSample:QuickContacts ID="QuickContacts1" Runat="server"
BorderStyle="Solid" BorderWidth="1px">
<aspSample:Contact Name="someone" Email="someone@example.com"
Phone="(555) 555-0100"/>
<aspSample:Contact Name="jae" Email="jae@fourthcoffee.com"
Phone="(555) 555-0101"/>
<aspSample:Contact Name="lene" Email="lene@contoso.com"
Phone="(555) 555-0102"/>
</aspSample:QuickContacts>
</form>
</body>
</html>
<%@ Page Language="C#"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
<title>
QuickContacts test page
</title>
</head>
<body>
<form id="Form1" runat="server">
<aspSample:QuickContacts ID="QuickContacts1" Runat="server"
BorderStyle="Solid" BorderWidth="1px">
<aspSample:Contact Name="someone" Email="someone@example.com"
Phone="(555) 555-0100"/>
<aspSample:Contact Name="jae" Email="jae@fourthcoffee.com"
Phone="(555) 555-0101"/>
<aspSample:Contact Name="lene" Email="lene@contoso.com"
Phone="(555) 555-0102"/>
</aspSample:QuickContacts>
</form>
</body>
</html>
生成和使用示例
使用集合编辑器示例中列出的 ContactCollectionEditor 编辑器对 QuickContacts 控件和 Contacts 类进行编译。必须添加对 System.Design 程序集的引用才能进行编译。
有关编译和使用自定义控件示例的更多信息,请参见生成自定义服务器控件示例。