为 ASP.NET 2.0 开发自定义数据绑定 Web 服务器控件

更新:2007 年 11 月

ASP.NET 数据绑定 Web 服务器控件为代表记录集合或项集合的数据源提供一个用户界面 (UI)。GridView Web 服务器控件概述DataList Web 服务器控件概述Repeater Web 服务器控件概述服务器控件是数据绑定 Web 服务器控件的示例。有关 ASP.NET 附带的数据绑定控件的更多信息,请参见 ASP.NET 数据绑定 Web 服务器控件概述

在 ASP.NET 版本 2.0 中,新的数据源模型使您能够将数据绑定控件绑定到源控件,从而允许将常规数据操作(如分页、排序和删除)从数据绑定控件本身中移出。此模型可以为页开发人员提供更灵活的数据绑定控件,并能够提高可重用性。

本主题介绍了实现自定义 ASP.NET 2.0 数据绑定服务器控件所需的基本步骤。有关自定义控件的通用结构和实现的更多信息,请参见开发自定义 ASP.NET 服务器控件演练:开发和使用自定义服务器控件。有关开发兼容 ASP.NET 1.1 的数据绑定控件的更多信息,请参见为 ASP.NET 1.1 开发自定义数据绑定 Web 服务器控件演练:为 ASP.NET 1.1 创建自定义数据绑定 ASP.NET Web 控件。有关数据源控件的更多信息,请参见数据源控件概述

何时创建自定义数据绑定控件

在创建自己的自定义数据绑定控件之前,请查看 ASP.NET 已附带的数据绑定控件的功能。现有控件可能已经满足您的需求,或者您可能决定创建自定义控件以扩展已提供所需的多个功能的现有控件。有关 ASP.NET 附带的数据绑定控件的更多信息,请参见 ASP.NET 数据绑定 Web 服务器控件概述

下面提供了可能促使您决定创建自定义数据绑定控件的一些原因:

  • 您的特定需求需要现有数据绑定控件中不具备的自定义 UI、自定义数据排序功能或自定义数据编辑功能。

  • 您希望创建预编译和可再发行的自定义数据绑定控件。

  • 您希望扩展 ASP.NET 已附带的数据绑定控件的功能。

  • 您希望创建带有符合您特定需求的自定义设计器的数据绑定控件。

自定义数据绑定控件的基本功能

通过从一个数据绑定控件基类(如 DataBoundControl 类或 CompositeDataBoundControl 类)派生,您的自定义数据绑定控件将自动继承许多内置功能,其中包括:

  • IDataSource 接口及其相关 DataSourceView 类的数据源支持,该功能提供数据的命名视图、查询数据存储区支持的操作类型的能力以及按需数据加载功能。

  • 对泛型 IEnumerableIListSource 接口的数据源支持。

  • 对数据绑定表达式的支持使得页开发人员能够在控件的特别标记的公开属性和数据源之间创建绑定。有关数据绑定表达式的更多信息,请参见数据绑定表达式概述

  • 对自动数据绑定的支持,该功能在页生命周期中尽可能晚地调用数据绑定逻辑,而且只在需要并非每次回发时调用。当页执行了第一个数据绑定请求后,后面的请求则尝试从视图状态检索数据。这样无需在每次请求时都重新连接到数据源,从而提高了性能。

使用可用设计时功能

您可能希望为自定义数据绑定控件考虑可用于所有 Web 服务器控件的设计时功能。您可以为自定义控件创建设计器类和控件模板。当您的控件在设计时在可视的设计图面(例如,Visual Studio 中的“设计”视图)上使用时将调用这些功能。

创建控件设计器可以在设计时提供能够使页开发人员自定义控件属性的设计时接口,来显著提高自定义控件的可用性。有关 ASP.NET 控件设计器的概述,请参见 ASP.NET 控件设计器概述。有关示例,请参见 HierarchicalDataBoundControlDesigner演练:为 Web 服务器控件创建基本控件设计器

通过创建模板化控件,可以使页开发人员灵活地指定用于定义控件的用户界面的控件和标记。有关自定义模板化控件的示例,请参见模板化服务器控件示例

在 ASP.NET 2.0 中实现自定义数据绑定控件

下表总结了特定于在 ASP.NET 2.0 中实现自定义数据绑定服务器控件的步骤。另外,在此表的后面,您还将找到有关每个实现步骤的更多详细信息。

步骤

步骤摘要

选择数据绑定控件基类。

扩展已提供所需的多个功能的现有数据绑定控件类,或者从一个数据绑定控件基类派生。

公开数据绑定属性。

配置自定义控件以公开除了由数据绑定控件基类公开的、所需的最低数据绑定属性之外的数据绑定属性。

启动数据检索。

  • 通过执行下列操作来检索由页开发人员分配给控件的数据:

  • 重写数据绑定控件基类的 PerformSelect 方法。

  • 调用数据绑定控件基类的 GetData 方法以检索控件的数据源视图

  • 调用 DataSourceView 对象的 Select 方法以启动数据检索并指定将接收该数据的回调方法。

处理检索的数据。

提供指定为 Select 方法中的参数的回调方法。回调方法必须包含 IEnumerable 类型的单个参数才能接收该数据。如果控件需要对数据进行任何处理,则它应出现在此回调方法内。

创建代表数据的 UI 对象

提供 PerformDataBinding 方法的重写。必须在此方法内执行下列任务:

  • 调用 PerformDataBinding 方法以允许执行依赖此方法的任何其他代码。

  • 枚举数据集合并创建将在 UI 显示中代表该数据的任何子控件。

选择数据绑定控件基类

控件开发人员可以扩展一个可用数据绑定控件基类来创建自定义数据绑定控件。下表提供了在 ASP.NET 2.0 中可用的数据绑定基类的列表。查看每个类的说明,然后确定哪一基类最符合自定义数据绑定控件的要求。有关数据绑定控件的每个基类和实现示例的更多详细信息,请参见每个类的参考文档。有关 ASP.NET 附带的数据绑定控件的更多信息,请参见 ASP.NET 数据绑定 Web 服务器控件概述

说明

BaseDataBoundControl

  • 此类为所有数据绑定控件的基类,用于执行数据绑定和验证任何绑定数据。

DataBoundControl

此类为用于生成标准数据绑定控件的数据绑定基类。DataBoundControl 提供下列功能:

  • 提供所有数据绑定控件共享的基本实现。

  • 包含用于与数据源控件和数据容器进行通信的逻辑。

  • 作为数据绑定控件(如 TreeViewListControl)的基础。

ListControl

  • 此类为列表控件的基类,扩展了 DataBoundControl 类。它提供 Items 集合和高级布局呈现功能。

CompositeDataBoundControl

  • 此类为扩展 DataBoundControl 类的基类,提供下列功能:

  • 实现复合控件所需的典型代码,包括在进行回发后从视图状态恢复控件的子控件层次结构的代码。

  • 绑定到 IEnumerable 数据源并枚举数据以生成控件树。

  • 作为数据绑定控件(如 GridViewDetailsView)的基础。

HierarchicalDataBoundControl

  • 此类为以分层的形式显示其数据的控件的基类。它作为基于树的数据绑定控件(如 TreeViewMenu)的基础。

提供的数据绑定属性

数据绑定控件基类已提供了使页开发人员能够将数据源绑定到控件所需的公开的数据绑定属性。控件作者无需进行其他工作。例如,通过从 DataBoundControl 派生,自定义控件便会继承三个公开的数据绑定属性:DataSourceIDDataSourceDataMember。然后,页开发人员可以通过设置 DataSourceDataSourceID 属性的值来指定控件将要绑定到的数据源。

利用 DataSourceID 属性,页开发人员可以指定代表数据绑定控件从中检索其数据的数据源的控件的 ID。

利用 DataSource 属性,页开发人员可以将数据绑定控件直接绑定到以下两种类型的数据对象:

  • 实现 IEnumerable 接口的对象(如 ArrayArrayListHashtable 对象)。

  • 实现 IListSource 接口的对象(如 DataSet 对象)。

  • 其他数据绑定属性(如 DataMember 属性)允许页开发人员指定控件应绑定到的数据集合的部分。

  • 有关每个数据绑定控件类或数据绑定基类所提供的公开的数据绑定属性的更多信息,请参见每个类的参考文档。

公开自定义数据绑定属性

在自定义控件中公开自己的自定义数据绑定属性或重写现有数据绑定属性时,如果已对控件进行了初始化,您应调用 OnDataPropertyChanged 方法。这可以强制数据绑定控件重新绑定数据,以便其能够使用新的数据绑定属性设置。

下面的代码示例演示属于派生的数据绑定控件类的属性。该示例演示如果初始化数据绑定控件后更改了标识数据源的属性,数据绑定控件应如何调用 OnDataPropertyChanged 方法。

Inherits DataBoundControl

Public Property DataTextField() As String
    Get
        Dim o As Object = ViewState("DataTextField")
        If o Is Nothing Then
            Return String.Empty
        Else
            Return CStr(o)
        End If
    End Get
    Set(ByVal value As String)
        ViewState("DataTextField") = value
        If (Initialized) Then
            OnDataPropertyChanged()
        End If
    End Set
End Property
public class SimpleDataBoundColumn : DataBoundControl
{
    public string DataTextField
    {
        get
        {
            object o = ViewState["DataTextField"];
            return ((o == null) ? string.Empty : (string)o);
        }
        set
        {
            ViewState["DataTextField"] = value;
            if (Initialized)
            {
                OnDataPropertyChanged();
            }
        }
    }

启动数据检索

在由控件的数据绑定基类继承的 PerformSelect 方法的重写内会启动数据检索。在此重写内,您调用该方法以检索数据并指定将在返回数据后便对其进行处理的回调方法。

若要检索数据,请在重写的 PerformSelect 方法内完成下列任务:

  1. 确定页开发人员是否使用 DataSource 属性或 DataSourceID 属性将数据设置为绑定到控件。这是通过验证 IsBoundUsingDataSourceID 属性完成的。例如,IsBoundUsingDataSourceID 属性的 false 设置指示使用了 DataSource 属性指定数据源。

  2. 如果页开发人员设置 DataSource 属性,那么需要额外的步骤:调用 OnDataBinding 方法。

  3. 调用 GetData 方法以检索与数据绑定控件关联的 DataSourceView 对象。

  4. 调用检索的 DataSourceViewSelect 方法以启动数据检索并指定将处理检索的数据的回调方法。Select 方法进行异步操作,因此在检索数据时可以进行其他处理。

  5. 通过将 RequiresDataBinding 属性设置为 false 并调用 MarkAsDataBound 方法来指示 PerformSelect 任务的完成。

  6. 引发 OnDataBound 事件。

下面的代码示例演示前面在 PerformSelect 方法的重写内完成的数据检索任务。

Protected Overrides Sub 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 Not IsBoundUsingDataSourceID Then
        OnDataBinding(EventArgs.Empty)
    End If

    ' 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)

End Sub
protected override void 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);
}

处理检索的数据

创建自己的回调方法以接受检索的数据。此方法是在 PerformSelect 方法的重写内调用 Select 方法时指定的回调方法。该回调方法必须只包含 IEnumerable 类型的单个参数。在回调方法中,如果控件要求,您可以对返回的数据进行任何处理。最后一个步骤,调用 PerformDataBinding 方法。

下面的代码示例演示具有 IEnumerable 类型的参数并调用 PerformDataBinding 方法的回调方法。

Private Sub 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)
    End If
    ' The PerformDataBinding method binds the data in the  
    ' retrievedData collection to elements of the data-bound control.
    PerformDataBinding(retrievedData)

End Sub
private void 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);
}

创建代表数据的 UI 对象

PerformDataBinding 方法的重写内,创建将代表该数据的子控件。枚举数据集合,并创建子控件并根据每个数据项设置其属性。通过将新的子控件添加到控件的 Controls 集合,可以为您呈现子控件。在控件的继承的 Render 方法中呈现控件层次结构。您可以选择重写 Render 方法以根据自定义控件的需求进行特定呈现,如包括用于在设计模式期间显示的其他 HTML 元素或特定呈现。

若要创建代表数据的 UI 对象,请重写 PerformDataBinding 方法并完成下列任务:

  1. 调用 PerformDataBinding 方法以允许执行依赖此方法的任何其他代码。

  2. 枚举数据集合并创建将在 UI 显示中代表该数据的任何子控件。通过调用控件的 Add 方法将每个子控件添加到控件的集合。

  • 下面的代码示例演示如何重写 PerformDataBinding 方法。调用 PerformDataBinding 方法以允许执行依赖此方法的任何其他代码。枚举数据集合,并创建子控件以在 UI 显示中代表数据。
        Protected Overrides Sub PerformDataBinding(ByVal retrievedData As IEnumerable)
            MyBase.PerformDataBinding(retrievedData)

            ' Verify data exists.
            If Not (retrievedData Is Nothing) Then
                Dim tbl As New Table()
                Dim row As TableRow
                Dim cell As TableCell
                Dim dataStr As String = String.Empty

                Dim dataItem As Object
                For Each 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)
                    Else
                        Dim props As PropertyDescriptorCollection = TypeDescriptor.GetProperties(dataItem)
                        If props.Count >= 1 Then
                            If Nothing <> props(0).GetValue(dataItem) Then
                                dataStr = props(0).GetValue(dataItem).ToString()
                            End If
                        End If
                    End If

                    row = New TableRow()
                    tbl.Rows.Add(row)
                    cell = New TableCell()
                    cell.Text = dataStr
                    row.Cells.Add(cell)
                Next dataItem

                Controls.Add(tbl)
            End If

        End Sub
    End Class
End Namespace
protected override void 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); 
    }
}

生成自定义服务器控件

有关生成自定义数据绑定 Web 服务器控件以及在网页中使用该控件的信息,请参见生成自定义服务器控件示例

请参见

任务

演练:为 ASP.NET 2.0 创建自定义数据绑定 ASP.NET Web 控件

演练:开发和使用自定义服务器控件

概念

ASP.NET 数据绑定 Web 服务器控件概述

自定义服务器控件的元数据属性

ASP.NET 控件设计器概述

参考

HierarchicalDataBoundControlDesigner

其他资源

开发自定义 ASP.NET 服务器控件