Web Control Collection Property Example
This example shows how to create a control named QuickContacts
that implements persistence in a page for a collection property. The example control is a control that allows a page developer to store a list of address book contacts. The QuickContacts
control exposes a Contacts
collection property that contains Contact
objects. The Contact
class has Name
, Email
, and Phone
properties.
The Contact
items of the Contacts
collection property are persisted within the control's tags as shown in the following example:
<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>
For clarity, the QuickContacts
control does not implement state management for the collection property. The collection items are assumed to be added declaratively in the page or, if created in code, they must be recreated on postback. In a production-quality control, you would implement state management. For details, see Server Control Custom State Management.
Code Listing for the QuickContacts Control
' 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;
}
}
}
Code Discussion
To enable parsing of the collection items within a control's tags, the QuickContacts
control adds the ParseChildren(true, "Contacts")
attribute to the control. The first argument (true) of ParseChildrenAttribute specifies that the page parser should interpret nested content within the control's tags as properties and not as child controls. The second argument ("Contacts"
) provides the name of the inner default property. When you specify the second argument, content within the control's tags must correspond to the default inner property (Contact
objects) and to nothing else.
The QuickContacts
control also includes the following design-time attributes that you must apply to a collection property for design-time serialization and persistence:
DesignerSerializationVisibilityAttribute Setting the Content parameter specifies that a visual designer should serialize the contents of the property. In the example, the property contains
Contact
objects.PersistenceModeAttribute Passing the InnerDefaultProperty parameter specifies that a visual designer should persist the property to which the attribute is applied as an inner default property. This means that a visual designer persists the property within the control's tags. The attribute can be applied to only one property, because only one property can be persisted within the control's tags. The property value is not wrapped in a special tag.
The QuickContacts
control associates a collection editor with the Contacts
collection property using the EditorAttribute, as in the following example:
Editor(typeof(ContactCollectionEditor), typeof(UITypeEditor))
Editor(GetType(ContactCollectionEditor), GetType(UITypeEditor))
Associating a collection editor with the property enables the property browser in a visual designer to open a collection editor for adding Contact
items. This is similar to the user interface (UI) for editing the Items property of the DropDownList or ListBox controls. The custom collection editor used by QuickContacts
, ContactCollectionEditor
, is described in Collection Editor Example.
For clarity, the QuickContacts
control does not define a strongly typed collection and instead uses an ArrayList for its collection type. In general, you should use a strongly typed collection as the type of the collection property so that an application developer cannot add arbitrary types to the collection.
Code Listing for the Contact Class
The design-time attributes in the code of the Contact
class are needed for property editing and design-time serialization. The ExpandableObjectConverter type converter associated with the Contact
class (using the TypeConverterAttribute) enables the collection editor to provide an expand/collapse user interface for editing subproperties (Name
, Email
, Phone
). This is similar to the UI you see when editing the Font property of a Web control in the property browser of a visual designer. The NotifyParentPropertyAttribute (with the constructor argument equal to true) applied to the Name
, Email
, and Phone
properties causes the editor to serialize changes in these properties into their parent property, an instance of the Contact
class.
' 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;
}
}
}
}
Test Page for the QuickContacts Control
The following example shows an .aspx page that uses the QuickContacts
control.
<%@ 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>
Building and Using the Example
Compile the QuickContacts
control and the Contacts
class with the ContactCollectionEditor
editor listed in Collection Editor Example. You must add a reference to the System.Design assembly for compilation.
For more information about compiling and using the custom control examples, see Building the Custom Server Control Examples.
See Also
Concepts
Collection Editor Example
Building the Custom Server Control Examples