数据绑定概述(Windows 窗体 .NET)

在 Windows 窗体中,你不仅可以绑定到传统的数据源,还可以绑定到几乎任何包含数据的结构。 可以绑定到你在运行时、从文件读取时或从其他控件的值派生时计算的一数组值。

此外,你可将任何控件的任何属性绑定到数据源。 在传统数据绑定中,你通常将显示属性(例如 Text 控件的 TextBox 属性)绑定到数据源。 通过 .NET,你还可选择通过绑定来设置其他属性。 你可以使用绑定来执行以下任务:

  • 设置图像控件的图形。

  • 设置一个或多个控件的背景色。

  • 设置控件的大小。

从根本上讲,数据绑定是一种设置窗体上任何控件的任何运行时可访问属性的自动方法。

使用 ADO.NET,可以创建许多不同的数据结构,以满足应用程序和正在处理的数据的绑定需要。 你可能希望创建自己的类,以便在 Windows 窗体中提供或使用数据。 这些对象可以提供各种级别的功能和复杂性。 从基本的数据绑定,到提供设计时支持、错误检查、更改通知,甚至是支持对数据本身所做更改的结构化回退。

数据绑定接口的使用者

以下部分描述两组接口对象。 第一组接口是数据源作者在数据源上实现的。 数据源使用者(如 Windows 窗体控件或组件)实现这些接口。 第二组接口设计为供组件作者使用。 组件作者在创建支持数据绑定的组件时使用这些接口,以便它们能够被 Windows 窗体数据绑定引擎使用。 可以在与窗体关联的类中实现这些接口以启用数据绑定。 每种情况都表示一个类,该类所实现的接口支持与数据进行交互。 Visual Studio 快速应用程序开发 (RAD) 数据设计体验工具已经利用了该功能。

要由数据源作者实现的接口

Windows 窗体控件实现以下接口:

  • IList 接口

    实现 IList 接口的类可以是 ArrayArrayListCollectionBase。 这些是类型 Object 的项的索引列表,列表必须包含同源类型,因为索引的第一项确定类型。 IList 仅在运行时可用于绑定。

    注意

    如果希望创建要与 Windows 窗体绑定的业务对象列表,则应当考虑使用 BindingList<T>BindingList 是可扩展的类,用于实现双向 Windows 窗体数据绑定所需的主要接口。

  • IBindingList 接口

    实现 IBindingList 接口的类提供一种更高级别的数据绑定功能。 此实现为你提供基本的排序功能和更改通知。 当列表项更改和列表本身发生更改时,这两者都很有用。 如果你计划将多个控件绑定到同一数据,则更改通知非常重要。 它可帮助你在其中一个控件中进行数据更改,以传播到其他绑定控件。

    注意

    更改通知是通过 SupportsChangeNotification 属性为 IBindingList 接口启用的,当该属性为 true 时,会引发一个 ListChanged 事件,指示列表或列表中的项已发生更改。

    更改的类型由 ListChangedEventArgs 参数的 ListChangedType 属性来描述。 因此,当数据模型进行更新时,任何依赖视图(例如,绑定到同一个数据源的其他控件)也会进行更新。 但是,包含在列表中的对象在更改时必须通知该列表,以便该列表引发 ListChanged 事件。

    注意

    BindingList<T> 提供 IBindingList 接口的泛型实现。

  • IBindingListView 接口

    实现 IBindingListView 接口的类提供 IBindingList 实现的所有功能以及筛选和高级排序功能。 此实现使用属性描述符-方向对提供基于字符串的筛选和多列排序能力。

  • IEditableObject 接口

    实现 IEditableObject 接口的类允许对象控制对该对象进行的更改何时成为永久更改。 此实现支持 BeginEditEndEditCancelEdit 方法,这些方法允许回滚对对象进行的更改。 下面简述了 BeginEditEndEditCancelEdit 方法的功能,以及它们如何搭配使用,以支持对数据更改进行可能的回滚:

    • BeginEdit 方法发出开始对一个对象进行编辑的信号。 实现此接口的对象需要存储 BeginEdit 方法调用后的任何更新,这样,如果调用 CancelEdit 方法,就可以放弃这些更新。 在数据绑定 Windows 窗体中,可以在单个编辑事务的范围内多次调用 BeginEdit(例如,BeginEditBeginEditEndEdit)。 IEditableObject 的实现应当跟踪 BeginEdit 是否已被调用,并忽略对 BeginEdit 的后续调用。 由于此方法可以多次调用,因此,对它的后续调用是非破坏性的,这一点很重要。 后续 BeginEdit 调用无法销毁已进行的更新或更改第一次 BeginEdit 调用时保存的数据。

    • 如果对象当前处于编辑模式,EndEdit 方法会将自调用 BeginEdit 后进行的任何更改都推送到基础对象中。

    • CancelEdit 方法放弃对对象所做的任何更改。

    有关 BeginEditEndEditCancelEdit 方法的工作原理的详细信息,请参阅将数据保存回数据库

    DataGridView 控件使用数据功能的这种事务性概念。

  • ICancelAddNew 接口

    实现 ICancelAddNew 接口的类通常实现 IBindingList 接口,并允许回滚用 AddNew 方法向数据源添加的数据。 如果数据源实现 IBindingList 接口,则还应当让其实现 ICancelAddNew 接口。

  • IDataErrorInfo 接口

    实现 IDataErrorInfo 接口的类允许对象向绑定控件提供自定义错误信息:

    • Error 属性返回常规错误消息文本(例如,“出现错误”)。

    • State 属性返回一个包含来自列的具体错误消息的字符串(例如,“Item[] 列中的值无效”)。

  • IEnumerable 接口

    实现 IEnumerable 接口的类通常由 ASP.NET 使用。 只能通过 BindingSource 组件来使用 Windows 窗体对此接口的支持。

    注意

    BindingSource 组件将所有的 IEnumerable 项复制到一个单独的列表中以实现绑定。

  • ITypedList 接口

    实现 ITypedList 接口的集合类提供如下功能:控制向绑定控件公开的属性集以及这些属性的顺序。

    注意

    在实现 GetItemProperties 方法时,如果 PropertyDescriptor 数组不为 null,则该数组中的最后一项为属性描述符,它描述作为另一个项列表的列表属性。

  • ICustomTypeDescriptor 接口

    实现 ICustomTypeDescriptor 接口的类提供有关其自身的动态信息。 此接口与 ITypedList 类似,但是它用于对象,而非列表。 此接口由 DataRowView 用来设计基础行的架构。 CustomTypeDescriptor 类提供 ICustomTypeDescriptor 的简单实现。

    注意

    若要支持到实现 ICustomTypeDescriptor 的类型的设计时绑定,该类型还必须实现 IComponent 并且作为实例存在于窗体上。

  • IListSource 接口

    实现 IListSource 接口的类在非列表对象上启用基于列表的绑定。 IListSourceGetList 方法用于从某个对象返回一个不是从 IList 继承的可绑定列表。IListSourceDataSet 类使用。

  • IRaiseItemChangedEvents 接口

    实现 IRaiseItemChangedEvents 接口的类是可绑定的列表,该列表还实现 IBindingList 接口。 此接口用于指示类型是否通过其 RaisesItemChangedEvents 属性引发 ItemChanged 类型的 ListChanged 事件。

    注意

    如果数据源提供用来列出前面描述的事件转换的属性,并且与 BindingSource 组件进行交互,则应当实现 IRaiseItemChangedEvents。 否则,BindingSource 还会执行用来列出事件转换的属性,这将导致性能下降。

  • ISupportInitialize 接口

    实现 ISupportInitialize 接口的组件利用批处理优化来设置属性并初始化共存属性。 ISupportInitialize 包含两种方法:

    • BeginInit 发出正在开始对象初始化的信号。

    • EndInit 发出正在完成对象初始化的信号。

  • ISupportInitializeNotification 接口

    实现 ISupportInitializeNotification 接口的组件还实现 ISupportInitialize 接口。 使用此接口,可以通知其他 ISupportInitialize 组件初始化已完成。 ISupportInitializeNotification 接口包含两个成员:

  • INotifyPropertyChanged 接口

    实现此接口的类是一个在其任何属性值更改时都会引发事件的类型。 此接口旨在替换控件的每个属性都有一个更改事件这种模式。 用在 BindingList<T> 中时,业务对象应当实现 INotifyPropertyChanged 接口,BindingList`1 会将 PropertyChanged 事件转换为 ItemChanged 类型的 ListChanged 事件。

    注意

    对于在绑定客户端和数据源之间的绑定中发生的更改通知,要么应当让绑定数据源类型实现 INotifyPropertyChanged 接口(首选方法),要么为绑定类型提供 propertyNameChanged 事件,但是不应同时选择这两种方法

要由组件作者实现的接口

下列接口设计为由 Windows 窗体数据绑定引擎使用:

Windows 窗体支持的数据源

传统上,已在应用程序中使用数据绑定,以利用存储在数据库中的数据。 使用 Windows 窗体数据绑定,只要满足某些最低要求,就可以访问数据库中的数据以及其他结构中的数据,例如数组和集合。

要绑定到的结构

在 Windows 窗体中,可以绑定到多种结构,从简单对象(简单绑定)到 ADO.NET 数据表等复杂列表(复杂绑定)。 对于简单绑定,Windows 窗体支持绑定到简单对象的公共属性。 Windows 窗体基于列表的绑定通常要求对象支持 IList 接口或 IListSource 接口。 此外,如果通过 BindingSource 组件进行绑定,则可以绑定到支持 IEnumerable 接口的对象。

以下列表显示了可以在 Windows 窗体中绑定到的结构。

  • BindingSource

    BindingSource 是最常见的 Windows 窗体数据源,它充当数据源和 Windows 窗体控件之间的代理。 常规 BindingSource 使用模式是将控件绑定到 BindingSource 并将 BindingSource 绑定到数据源(例如,ADO.NET 数据表或业务对象)。 BindingSource 提供启用和提高数据绑定支持级别的服务。 例如,Windows 窗体基于列表的控件(例如 DataGridViewComboBox)不直接支持绑定到 IEnumerable 数据源,但是,可以通过 BindingSource 绑定来启用此方案。 在这种情况下,BindingSource 会将数据源转换为 IList

  • 简单对象

    Windows 窗体支持数据绑定控件属性到使用 Binding 类型的对象实例上的公共属性。 Windows 窗体还支持基于列表的控件绑定,例如在使用 BindingSource 时将 ListControl 绑定到对象实例。

  • 数组或集合

    若要充当数据源,列表必须实现 IList 接口;例如数组,它是 Array 类的一个实例。 有关数组的更多信息,请参阅如何:创建对象数组 (Visual Basic)

    通常,为数据绑定创建对象列表时,应使用 BindingList<T>BindingListIBindingList 接口的泛型版本。 IBindingList 接口通过添加双向数据绑定所需的属性、方法和事件来扩展 IList 接口。

  • IEnumerable

    Windows 窗体控件可以绑定到仅支持 IEnumerable 接口的数据源,前提是它们通过 BindingSource 组件绑定。

  • ADO.NET 数据对象

    ADO.NET 提供了许多适合绑定的数据结构。 每一种的复杂程度都有所不同。

    • DataColumn

      DataColumnDataTable 的基本构建基块,其中多个列构成一个表。 每个 DataColumn 都有一个 DataType 属性,用于确定该列包含的数据类型(例如,描述汽车的表中的汽车品牌)。 可以将控件(例如 TextBox 控件的 Text 属性)简单绑定到数据表中的列。

    • DataTable

      DataTable 是 ADO.NET 中包含行和列的表的表示形式。 一个数据表包含两个集合:DataColumn 表示给定表中的数据列(最终确定可以输入到该表中的数据种类),DataRow 表示给定表中的数据行。 可以将控件复杂绑定到数据表中包含的信息(例如将 DataGridView 控件绑定到数据表)。 但是,绑定到 DataTable 时,会绑定到表的默认视图。

    • DataView

      DataView 是单个数据表的自定义视图,可对其进行筛选或排序。 数据视图是复杂绑定控件使用的数据“快照”。 可以简单绑定或复杂绑定到数据视图中的数据,但请注意,要绑定到的是数据的固定“图片”,而不是干净、更新中的数据源。

    • DataSet

      DataSet 是数据库中数据的表、关系和约束的集合。 可以简单绑定或复杂绑定到数据集中的数据,但请注意,要绑定到 DataSet 的默认 DataViewManager(请参阅下一个要点)。

    • DataViewManager

      DataViewManager 是整个 DataSet 的自定义视图,类似于 DataView,但包含关系。 使用 DataViewSettings 集合,可以为给定表的 DataViewManager 的任何视图设置默认筛选器和排序选项。

数据绑定的类型

Windows 窗体可以利用两种类型的数据绑定:简单绑定和复杂绑定。 每一种都有不同的优势。

数据绑定的类型 说明
简单数据绑定 控件绑定到单个数据元素(如数据集表的列中的值)的能力。 简单数据绑定是 TextBox 控件或 Label 控件等控件(即通常只显示单个值的控件)的典型绑定类型。 事实上,控件上的任何属性都可以绑定到数据库中的字段。 Visual Studio 中提供对此功能的广泛支持。

有关详细信息,请参阅导航数据创建简单绑定控件(Windows 窗体 .NET)
复杂数据绑定 控件绑定一个以上数据元素(通常为一个数据库中的一个以上的记录)的能力。 复杂绑定也称基于列表的绑定。 支持复杂绑定的控件示例为 DataGridViewListBoxComboBox 控件。 有关复杂数据绑定的示例,请参阅如何:将 Windows 窗体 ComboBox 控件或 ListBox 控件绑定到数据

绑定源组件

为了简化数据绑定,Windows 窗体让你可将数据源绑定到 BindingSource 组件,然后将控件绑定到 BindingSource。 你可以在简单或复杂绑定方案中使用 BindingSource。 在任一种情况下,BindingSource 均充当数据源和绑定控件的中介,提供更改通知货币管理和其他服务。

采用数据绑定的常见方案

几乎每个商业应用程序都使用读取自一种类型或另一种类型的数据源的信息,方法通常是数据绑定。 以下列表显示了几个最常见的利用数据绑定作为数据表示和操作方法的方案。

方案 说明
正在报告 报表为你提供了一种灵活的方式来显示和汇总打印出的文档中的数据。 一种常见的做法是:创建一个将数据源的选定内容打印到屏幕或打印机的报表。 常见的报表包括列表、发票和摘要。 将项格式化成列表的列,在每个列表项下组织子项,但你应选择最适合数据的布局。
数据输入 输入大量相关数据或提示用户输入信息的常用方法是使用数据输入表单。 用户可以使用文本框、选项按钮、下拉列表和复选框来输入信息或选择选项。 然后提交信息并将其存储在数据库中,数据库结构基于所输入的信息。
大纲/细节关系 大纲/细节应用程序是查看相关数据的一种格式。 具体来说,在经典业务示例中,有两个具有连接关系的数据表,“顾客”表和“订单”表之间存在联系客户和对应订单的关系。 有关使用两个 Windows 窗体 DataGridView 控件创建大纲/细节应用程序的详细信息,请参阅如何:使用两个 Windows 窗体 DataGridView 控件创建一个主/从窗体
查找表 另一个常见的数据表示/操作方案是表查找。 通常情况下,作为较大数据显示的一部分,ComboBox 控件用于显示和操作数据。 关键在于 ComboBox 控件中显示的数据与写入数据库中的数据不同。 例如,如果你有一个显示杂货店中的物料的 ComboBox 控件,你可能想要查看产品名称(面包、牛奶、鸡蛋)。 但是,为了便于在数据库中检索信息或使数据库标准化,你可能会将给定订单特定项的信息存储为物料编号(#501、#603 等等)。 因此,你窗体上的 ComboBox 控件中的杂货物料“友好名称”和存在于订单中的物料编号间有着隐式联系。 这就是表查找的实质。 有关详细信息,请参阅如何:使用 Windows 窗体 BindingSource 组件创建查找表

另请参阅