Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
It is easy to bind a property of a control to a single data item (or expression) using ASP.NET data-binding syntax. This section addresses the more complex scenario of developing a control that has templated properties bound to a data source that is a collection type (System.Collections.ICollection or System.Collections.IEnumerable). Templates enable a page developer to customize the presentation of data that is bound to the control. The Repeater and DataList controls are examples of templated data-bound controls.
For an overview of data binding in ASP.NET pages, see the ASP.NET QuickStart —> ASP.NET Web Forms —> Databinding Server Controls. For background on authoring a templated control, see Developing a Templated Control.
A templated data-bound control has a data source property of type ICollection or IEnumerable and one or more properties of type ITemplate. The container for one of the template properties defines a property (generally named DataItem
) to bind data to. The control implements its data-binding logic in the Databind method that it inherits from Control. It overrides the CreateChildControls method to recreate the hierarchy of child controls upon postback. These steps are explained in greater detail in the following discussion.
To develop a templated data-bound control
Define a control that implements the System.Web.UI.INamingContainer interface.
public class TemplatedList : WebControl, INamingContainer {...} [Visual Basic] Public Class TemplatedList Inherits WebControl Implements INamingContainer ... End Class
Define a property of type System.Web.UI.ITemplate.
[TemplateContainer(typeof(TemplatedListItem))] public virtual ITemplate ItemTemplate { get { return itemTemplate; } set { itemTemplate = value; } } [Visual Basic] <TemplateContainer(GetType(TemplatedListItem))> _ Public Overridable Property ItemTemplate() As ITemplate Get Return _itemTemplate End Get Set _itemTemplate = value End Set End Property
The logical container for the template (specified in the TemplateContainerAttribute attribute) must have a property to bind data to. By convention, this property is named DataItem. For details about logical containers for template properties, see Developing a Templated Control. The following example defines a container for the template property.
public class TemplatedListItem : TableRow, INamingContainer { private object dataItem; public virtual object DataItem { get { return dataItem; } set { dataItem = value; } } [Visual Basic] Public Class TemplatedListItem Inherits TableRow Implements INamingContainer Private _dataItem As Object Public Overridable Property DataItem() As Object Get Return _dataItem End Get Set _dataItem = value End Set End Property End Class
Override the DataBind method (inherited from Control) to provide data-binding logic. This must have the following steps:
- Invoke the OnDataBinding method of the base class to invoke the handlers (attached by the page) that evaluate data-binding expressions on your control.
- Clear the Controls collection.
- Clear the ViewState of the child controls.
- Create the child controls using the data source.
- Signal to the ASP.NET page framework to track the ViewState for your control.
The following code performs these steps.
CreateChildControlsHierarchy
is a helper method to perform the actual work of creating the child controls. See step 5 for details.public override void DataBind() { // Controls with a data-source property perform their // custom data binding by overriding DataBind to // evaluate any data-binding expressions on the control // itself. base.OnDataBinding(EventArgs.Empty); // Reset the control's state. Controls.Clear(); ClearChildViewState(); // Create the control hierarchy using the data source. CreateControlHierarchy(true); ChildControlsCreated = true; TrackViewState(); } [Visual Basic] Public Overrides Sub DataBind() ' Controls with a data-source property perform their custom data ' binding by overriding DataBind. ' Evaluate any data-binding expressions on the control itself. MyBase.OnDataBinding(EventArgs.Empty) ' Reset the control state. Controls.Clear() ClearChildViewState() ' Create the control hierarchy using the data source. CreateControlHierarchy(True) ChildControlsCreated = True TrackViewState() End Sub
Override CreateChildControls to recreate the child controls in a postback scenario. This involves clearing the Controls collection and creating the control hierarchy using the view state instead of the data source. The actual work of creating the child controls is hidden in the
CreateControlHierarchy
method described in step 5.protected override void CreateChildControls() { Controls.Clear(); if (ViewState["ItemCount"] != null) { // Create the control hierarchy using the view state, // not the data source. CreateControlHierarchy(false); } } [Visual Basic] Protected Overrides Sub CreateChildControls() Controls.Clear() If Not (ViewState("ItemCount") Is Nothing) Then ' Create the control hierarchy using the view state, ' not the data source. CreateControlHierarchy(False) End If End Sub
Define a data source that has null elements and use this data source instead of the real data source when creating the control hierarchy on postback. Steps 3 and 4 create the controls hierarchy using the data source and the saved view state, respectively. A dummy data source enables a control to implement a single code path for the common elements of these two steps.
Note This step (step 5) describes implementation details used by the data-bound ASP.NET controls in the .NET Framework. The
DummyDataSource
class and the
CreateControlHierarchy
method shown in the following fragment are not in the .NET Framework but have to be defined by a control developer. You are not required to implement these elements; however, it is recommended that you use this or a similar technique to provide a common code path for creating the control hierarchy.
The following code fragment defines a dummy data source.
internal sealed class DummyDataSource : ICollection { private int dataItemCount; public DummyDataSource(int dataItemCount) { this.dataItemCount = dataItemCount; } // Implement other methods of the ICollection interface. ... public IEnumerator GetEnumerator() { return new DummyDataSourceEnumerator(dataItemCount); } private class DummyDataSourceEnumerator : IEnumerator { private int count; private int index; public DummyDataSourceEnumerator(int count) { this.count = count; this.index = -1; } public object Current { get { return null; } } // Define other methods of the IEnumerator interface. } } [Visual Basic] NotInheritable Friend Class DummyDataSource Implements ICollection Private dataItemCount As Integer Public Sub New(dataItemCount As Integer) Me.dataItemCount = dataItemCount End Sub ' Implement other methods of the ICollection interface. ... Public Function GetEnumerator() As IEnumerator Implements ICollection.GetEnumerator Return New DummyDataSourceEnumerator(dataItemCount) End Function Private Class DummyDataSourceEnumerator Implements IEnumerator Private count As Integer Private index As Integer Public Sub New(count As Integer) Me.count = count Me.index = - 1 End Sub Public ReadOnly Property Current() As Object Implements IEnumerator.Current Get Return Nothing End Get End Property ' Define other methods of the IEnumerator interface. ... End Class End Class
DummyDataSource
can be used to define theCreateControlHierarchy
method, as follows.private void CreateControlHierarchy(bool useDataSource) { IEnumerable dataSource = null; int count = -1; if (useDataSource == false) { // ViewState must have a non-null value for ItemCount because this is checked // by CreateChildControls. count = (int)ViewState["ItemCount"]; if (count != -1) { dataSource = new DummyDataSource(count); } } else { dataSource = this.dataSource; } if (dataSource != null) { int index = 0; count = 0; foreach (object dataItem in dataSource) { ... // Invoke a private helper method to create each item. CreateItem(...); count++; index++; } } if (useDataSource) { // Save the number of items contained for use in round trips. ViewState["ItemCount"] = ((dataSource != null) ? count : -1); } } [Visual Basic] Private Sub CreateControlHierarchy(useDataSource As Boolean) Dim dataSource As IEnumerable = Nothing Dim count As Integer = - 1 If useDataSource = False Then ' ViewState must have a non-null value for ItemCount because this is checked ' by CreateChildControls. count = CInt(ViewState("ItemCount")) If count <> - 1 Then dataSource = New DummyDataSource(count) End If Else dataSource = Me._dataSource End If If Not (dataSource Is Nothing) Then Dim table As New Table() Controls.Add(table) Dim selectedItemIndex As Integer = SelectedIndex Dim index As Integer = 0 count = 0 Dim dataItem As Object For Each dataItem In dataSource Dim itemType As ListItemType = ListItemType.Item If index = selectedItemIndex Then itemType = ListItemType.SelectedItem Else If index Mod 2 <> 0 Then itemType = ListItemType.AlternatingItem End If End If CreateItem(table, index, itemType, useDataSource, dataItem) count += 1 index += 1 Next dataItem End If If useDataSource Then ' Save the number of items contained for use in round trips. If Not (dataSource Is Nothing) Then ViewState("ItemCount") = count Else ViewState("ItemCount") = -1 End If End If End Sub
The
CreateItem
method does the actual work of creating the template and binding theDataItem
property to the data source. The following code fragment shows how theCreateItem
method is implemented in the Templated Data-Bound Control Sample. Note that theCreateItem
method is an implementation detail and is not defined in the .NET Framework.private TemplatedListItem CreateItem(Table table, int itemIndex, ListItemType itemType, bool dataBind, object dataItem) { TemplatedListItem item = new TemplatedListItem(itemIndex, itemType); TemplatedListItemEventArgs e = new TemplatedListItemEventArgs(item); if (itemTemplate != null) { itemTemplate.InstantiateIn(item.Cells[0]); } if (dataBind) { item.DataItem = dataItem; } OnItemCreated(e); table.Rows.Add(item); if (dataBind) { item.DataBind(); OnItemDataBound(e); item.DataItem = null; } return item; } [Visual Basic] Private Function CreateItem(table As Table, itemIndex As Integer, itemType As ListItemType, dataBind As Boolean, dataItem As Object) As TemplatedListItem Dim item As New TemplatedListItem(itemIndex, itemType) Dim e As New TemplatedListItemEventArgs(item) If Not (_itemTemplate Is Nothing) Then _itemTemplate.InstantiateIn(item.Cells(0)) End If If dataBind Then item.DataItem = dataItem End If OnItemCreated(e) table.Rows.Add(item) If dataBind Then item.DataBind() OnItemDataBound(e) item.DataItem = Nothing End If Return item End Function
For a sample of a data-bound control that implements the steps discussed in this topic, see the Templated Data-Bound Control Sample.