Developing Custom Data-Bound Web Server Controls for ASP.NET 2.0
ASP.NET data-bound Web server controls provide a user interface (UI) for a data source representing a collection of records or items. The GridView Web Server Control Overview, DataList Web Server Control Overview, and Repeater Web Server Control Overview server controls are examples of data-bound Web server controls. For more information about data-bound controls included with ASP.NET, see ASP.NET Data-Bound Web Server Controls Overview.
In ASP.NET version 2.0, a new data source model enables you to bind data-bound controls to source controls, permitting common data operations — such as paging, sorting, and deleting — to be moved out of the data-bound control itself. This model yields a more flexible data-bound control for page developers and increases the level of reusability.
This topic introduces the basic steps that are required to implement a custom ASP.NET 2.0 data-bound server control. For more information about the general architecture and implementations of custom controls, see Developing Custom ASP.NET Server Controls and Walkthrough: Developing and Using a Custom Web Server Control. For more information about developing ASP.NET 1.1 compatible data-bound controls, see Developing Custom Data-Bound Web Server Controls for ASP.NET 1.1 and Walkthrough: Creating a Custom Data-Bound ASP.NET Web Control for ASP.NET 1.1. For more information about data source controls, see Data Source Controls Overview.
When to Create a Custom Data-Bound Control
Before creating your own custom data-bound control, review the capabilities of the data-bound controls already provided with ASP.NET. Existing controls might meet your needs, or you might decide to create a custom control that extends an existing control which already provides many of the features you need. For more information about data-bound controls provided with ASP.NET see, ASP.NET Data-Bound Web Server Controls Overview.
Here are some reasons you might decide to create a custom data-bound control:
Your particular needs require custom UI, custom data-sorting features, or custom data-editing features that are not available in existing data-bound controls.
You want to create a custom data-bound control that that is pre-compiled and redistributable.
You want to extend the features of a data-bound control that is already provided with ASP.NET.
You want to create a data-bound control with a custom designer that fits your specific needs.
Basic Functionality of a Custom Data-Bound Control
By deriving from one of the data-bound control base classes, such as the DataBoundControl class or the CompositeDataBoundControl class, your custom data-bound control automatically inherits many built-in features, including the following:
Data source support for the IDataSource interface and its related DataSourceView class, which provides named views of data, the ability to query the types of operations supported by the data store, and on-demand data loading functionality.
Data source support for the generic IEnumerable or IListSource interfaces.
Support for data-binding expressions, which enables page developers to create bindings between an exposed, specially marked property of your control and a data source. For more information about data-binding expressions, see Data-Binding Expressions Overview.
Support for automatic data binding that invokes data-binding logic as late as possible during the page life cycle and only when needed, rather than on each postback. After a page performs the first data-binding request, subsequent requests attempt to retrieve the data from view state. This enhances performance by avoiding the need to reconnect to the data source on every request.
Utilizing Available Design Time Features
There are design-time features available to all Web server controls that you might want to consider for your custom data-bound control. You can create a designer class and control templates for your custom control. These features are invoked when your control is used at design time on a visual design surface, such as Design view in Visual Studio.
Creating a control designer can greatly increase your custom control’s usability at design time by providing a design-time interface that enables page developers to customize control properties. For an overview of ASP.NET control designers, see ASP.NET Control Designers Overview. For examples, see HierarchicalDataBoundControlDesigner and Walkthrough: Creating a Basic Control Designer for a Web Server Control.
By creating a templated control, you provide page developers the flexibility to specify the controls and markup that define control's user interface. For an example of a custom templated control, see Templated Server Control Example.
Implementing a Custom Data-Bound Control in ASP.NET 2.0
The following table summarizes the steps that are specific to implementing a custom data-bound server control in ASP.NET 2.0. After the table, you will find more detailed information about each of the implementation steps.
Step |
Step Summary |
---|---|
Choose a data-bound control base class. |
Extend an existing data-bound control class that already provides many of the features you need, or derive from one of the base data-bound control classes. |
Expose data-binding properties. |
Configure your custom control to expose data-binding properties that are in addition to the minimum required data-binding properties exposed by base data-bound control classes. |
Initiate data retrieval. |
|
Handle the retrieved data. |
Provide the callback method that you specified as a parameter in the Select method. The callback method must have a single parameter of type IEnumerable to receive the data. If any processing of the data is required by your control, it should occur within this callback method. |
Create the UI objects representing the data |
Provide an override of the PerformDataBinding method. You must execute the following tasks within this method:
|
Choose a Data-Bound Control Base Class
Control developers can extend one of the available data-bound control base classes to create a custom data-bound control. The following table provides a list of data-bound base classes that are available in ASP.NET 2.0. Review the descriptions of each class and then determine which base class best fits the requirements of your custom data-bound control. For more detailed information about each base class for data-bound controls and implementation examples, see the reference documentation for each class. For more information about data-bound controls provided by ASP.NET, see ASP.NET Data-Bound Web Server Controls Overview.
Class |
Description |
---|---|
|
|
This is the base data-bound class for building standard data-bound controls. DataBoundControl does the following:
|
|
|
|
|
|
Provided Data-Binding Properties
The base data-bound control classes already provide the exposed data-binding properties needed to enable a page developer to bind a data source to a control. No additional work is needed on the part of the control author. By deriving from DataBoundControl, for example, a custom control inherits three exposed data-binding properties: DataSourceID, DataSource, and DataMember. A page developer can then specify the source of the data to which the control will bind by setting the value of either the DataSource or the DataSourceID properties.
The DataSourceID property enables the page developer to specify the ID of a control that represents the data source from which the data-bound control retrieves its data.
The DataSource property enables the page developer to bind a data-bound control directly to data-objects of these two types:
An object that implements the IEnumerable interface, such as an Array, ArrayList, or Hashtable object.
An object that implements the IListSource interface, such as a DataSet object.
Additional data-binding properties, such as the DataMember property, allow the page developer to specify the portion of the data collection to which the control should bind.
For more information about the exposed data-binding properties provided by each data-bound control class or data-bound base class, see the reference documentation for each class.
Exposing Custom Data-Binding properties
When exposing your own custom data-binding properties in a custom control or overriding existing data-binding properties, you should call the OnDataPropertyChanged method if the control has already been initialized. This forces the data-bound control to rebind the data so it can use the new data-binding property setting.
The following code example shows a property that belongs to a derived data-bound control class. The example demonstrates how a data-bound control can call the OnDataPropertyChanged method if a property that identifies a data source is changed after the data-bound control is initialized.
Inherits DataBoundControl
PublicProperty DataTextField() AsStringGetDim o AsObject = ViewState("DataTextField")
If o IsNothingThenReturnString.Empty
ElseReturnCStr(o)
EndIfEndGetSet(ByVal value AsString)
ViewState("DataTextField") = value
If (Initialized) Then
OnDataPropertyChanged()
EndIfEndSetEndProperty
publicclass SimpleDataBoundColumn : DataBoundControl
{
publicstring DataTextField
{
get
{
object o = ViewState["DataTextField"];
return ((o == null) ? string.Empty : (string)o);
}
set
{
ViewState["DataTextField"] = value;
if (Initialized)
{
OnDataPropertyChanged();
}
}
}
Initiate Data Retrieval
Data retrieval is initiated within an override of the PerformSelect method inherited by your control's base data-bound control. Within this override you call to retrieve the data and specify a callback method that will handle the data once it is returned.
To retrieve data, complete the following tasks in the overridden PerformSelect method:
Determine if the page developer used the DataSource property or the DataSourceID property to set the data to be bound to the control. This is done by verifying the IsBoundUsingDataSourceID property. For example, a false setting for the IsBoundUsingDataSourceID property indicates that the DataSource property was used to specify the data source.
If the page developer set the DataSource property, then an extra step is required: Call the OnDataBinding method.
Call the GetData method to retrieve the DataSourceView object associated with the data-bound control.
Call the Select method of the retrieved DataSourceView to initiate data retrieval and specify the callback method that will handle the retrieved data. The Select method operates asynchronously, so other processing is allowed while the data is being retrieved.
Indicate the completion of the PerformSelect tasks by setting the RequiresDataBinding property to false and then calling the MarkAsDataBound method.
Raise the OnDataBound event.
The following code example illustrates the preceding data-retrieval tasks as completed within an override of the PerformSelect method.
ProtectedOverridesSub PerformSelect()
' Call OnDataBinding here if bound to a data source using the ' DataSource property (instead of a DataSourceID), because the ' databinding statement is evaluated before the call to GetData. IfNot IsBoundUsingDataSourceID Then
OnDataBinding(EventArgs.Empty)
EndIf
' The GetData method retrieves the DataSourceView object from ' the IDataSource associated with the data-bound control.
GetData().Select(CreateDataSourceSelectArguments(), _
AddressOf OnDataSourceViewSelectCallback)
' The PerformDataBinding method has completed.
RequiresDataBinding = False
MarkAsDataBound()
' Raise the DataBound event.
OnDataBound(EventArgs.Empty)
EndSub
protectedoverridevoid PerformSelect()
{
// Call OnDataBinding here if bound to a data source using the// DataSource property (instead of a DataSourceID), because the// databinding statement is evaluated before the call to GetData. if (!IsBoundUsingDataSourceID)
{
this.OnDataBinding(EventArgs.Empty);
}
// The GetData method retrieves the DataSourceView object from // the IDataSource associated with the data-bound control.
GetData().Select(CreateDataSourceSelectArguments(),
this.OnDataSourceViewSelectCallback);
// The PerformDataBinding method has completed.
RequiresDataBinding = false;
MarkAsDataBound();
// Raise the DataBound event.
OnDataBound(EventArgs.Empty);
}
Handle the Retrieved Data
Create your own callback method to accept the retrieved data. This is the callback method you specified when the Select method was called within the override of the PerformSelect method. The callback method is required to contain only a single parameter of the type IEnumerable. In your callback method you can do any processing of the returned data, if required by your control. As a last step, call the PerformDataBinding method.
The following code example illustrates a callback method that has a parameter of type IEnumerable and calls the PerformDataBinding method.
PrivateSub OnDataSourceViewSelectCallback(ByVal retrievedData As IEnumerable)
' Call OnDataBinding only if it has not already been ' called in the PerformSelect method.If IsBoundUsingDataSourceID Then
OnDataBinding(EventArgs.Empty)
EndIf ' The PerformDataBinding method binds the data in the ' retrievedData collection to elements of the data-bound control.
PerformDataBinding(retrievedData)
EndSub
privatevoid OnDataSourceViewSelectCallback(IEnumerable retrievedData)
{
// Call OnDataBinding only if it has not already been // called in the PerformSelect method.if (IsBoundUsingDataSourceID)
{
OnDataBinding(EventArgs.Empty);
}
// The PerformDataBinding method binds the data in the // retrievedData collection to elements of the data-bound control.
PerformDataBinding(retrievedData);
}
Create the UI Objects Representing the Data
Within an override of the PerformDataBinding method, you create the child controls that will represent the data. The data collection is enumerated, and the child controls are created and their properties set based on each data item. By adding the new child controls to the control's Controls collection, the child controls will be rendered for you. The control hierarchy renders during the control's inherited Render method. You might choose to override the Render method to do special rendering required by your custom control, such as including additional HTML elements or special rendering for display during design mode.
To create the UI objects representing the data, override the PerformDataBinding method and complete the following tasks:
Call the PerformDataBinding method to allow any other code relying on this method to execute.
Enumerate through the data collection and create any child controls that will represent the data in the UI display. Add each child control to the control's collection by calling the control's Add method.
- The following code example illustrates overriding the PerformDataBinding method. The PerformDataBinding method is called to allow any other code relying on this method to execute. The data collection is enumerated and child controls are created to represent the data in the UI display.
ProtectedOverridesSub PerformDataBinding(ByVal retrievedData As IEnumerable)
MyBase.PerformDataBinding(retrievedData)
' Verify data exists.IfNot (retrievedData IsNothing) ThenDim tbl AsNew Table()
Dim row As TableRow
Dim cell As TableCell
Dim dataStr AsString = String.Empty
Dim dataItem AsObjectForEach dataItem In retrievedData
' If the DataTextField was specified get the data ' from that field, otherwise get the data from the first field. If DataTextField.Length > 0 Then
dataStr = DataBinder.GetPropertyValue(dataItem, DataTextField, Nothing)
ElseDim props As PropertyDescriptorCollection = TypeDescriptor.GetProperties(dataItem)
If props.Count >= 1 ThenIfNothing <> props(0).GetValue(dataItem) Then
dataStr = props(0).GetValue(dataItem).ToString()
EndIfEndIfEndIf
row = New TableRow()
tbl.Rows.Add(row)
cell = New TableCell()
cell.Text = dataStr
row.Cells.Add(cell)
Next dataItem
Controls.Add(tbl)
EndIfEndSubEndClassEndNamespace
protectedoverridevoid PerformDataBinding(IEnumerable retrievedData)
{
base.PerformDataBinding(retrievedData);
// Verify data exists.if (retrievedData != null)
{
Table tbl = new Table();
TableRow row;
TableCell cell;
string dataStr = String.Empty;
foreach (object dataItem in retrievedData)
{
// If the DataTextField was specified get the data// from that field, otherwise get the data from the first field. if (DataTextField.Length > 0)
{
dataStr = DataBinder.GetPropertyValue(dataItem,
DataTextField, null);
}
else
{
PropertyDescriptorCollection props =
TypeDescriptor.GetProperties(dataItem);
if (props.Count >= 1)
{
if (null != props[0].GetValue(dataItem))
{
dataStr = props[0].GetValue(dataItem).ToString();
}
}
}
row = new TableRow();
tbl.Rows.Add(row);
cell = new TableCell();
cell.Text = dataStr;
row.Cells.Add(cell);
}
this.Controls.Add(tbl);
}
}
Building Your Custom Server Control
For information about building your custom data-bound Web server control and using it in a Web page, see Building the Custom Server Control Examples.
See Also
Tasks
Walkthrough: Creating a Custom Data-Bound ASP.NET Web Control
Walkthrough: Developing and Using a Custom Web Server Control
Concepts
ASP.NET Data-Bound Web Server Controls Overview
Metadata Attributes for Custom Server Controls
ASP.NET Control Designers Overview
Reference
HierarchicalDataBoundControlDesigner