Windows 窗体应用程序中的数据绑定
简单数据绑定
简单数据绑定是单个控件属性和一个求值的可计算表达式之间的一对一关联。您实际上可以使用任何求值的表达式,但在多数情况下,您将绑
定到某种数据源。
复杂数据绑定
复杂数据绑定将某个给定控件的整个界面与从数据源读取的数据的一个或多个列关联起来。例如,DataGrid 控件的用户界面是用许多数据库列
来填充的。
Windows 窗体属性数据绑定
现在我们来进一步了解一下 Windows 窗体控件是如何实现简单数据绑定的。Windows 窗体数据绑定控件维护绑定列表中所有其数据绑定属性。
这些绑定集中在一个名为 DataBindings 的集合属性中。控件加载后,立即定义动态求值的表达式,并为每个绑定数据的属性提供实际值。
DataBindings 列表只设置一次,并始终保持不变,而且更重要的是,它是以位置不可知的方式定义的。实际上,Windows 窗体数据绑定控件无
需知道关于它绑定到的当前数据源行的信息。关于数据绑定,控件唯一要做的就是声明它自己的数据绑定列表。除此之外,控件将数据保持为
同步和最新所需进行的任何额外工作都交给框架。
DataBindings 映射绑定属性。
一旦您提取执行该应用程序所需的所有数据,就将控件绑定到数据绑定表达式。下面的代码将结果集加载到一个名为 “雇员” 的数据表和名
为 m_ds 的父数据集的子数据集。该数据集已声明为窗体类的私有数据成员。它在窗体加载过程中实例化。
Dim da As SqlDataAdapter
da = New SqlDataAdapter("SELECT * FROM Employees", _
"SERVER=localhost;DATABASE=northwind;UID=sa;")
da.Fill(m_ds, "Employees")
该窗体包含四个 TextBox 控件和一个 PictureBox 控件,所有控件必须绑定到数据。将控件的属性绑定到数据元素需要创建一个 Binding 对
象。Binding 类表示数据对象的值和控件属性之间的简单链接。类构造函数需要三个参数 — 属性名、数据源以及在当前数据项中选择值的表
达式
下面列出的内容显示了用于将窗体的控件绑定到其数据源的代码。
Private Sub BindControls()
Dim b1 As Binding
b1 = New Binding("Text", m_ds, "Employees.FirstName")
firstName.DataBindings.Add(b1)
Dim b2 As Binding
b2 = New Binding("Text", m_ds, "Employees.LastName")
lastName.DataBindings.Add(b2)
Dim b3 As Binding
b3 = New Binding("Text", m_ds, "Employees.Title")
position.DataBindings.Add(b3)
Dim b4 As Binding
b4 = New Binding("Text", m_ds, "Employees.HireDate")
hired.DataBindings.Add(b4)
' Store the instance of the binder object for the control bound to
' the Employees table in the given DataSet
m_bmbEmployees = Me.BindingContext(m_ds, "Employees")
End Sub
绑定代码中没有明确指出记录的位置。也就是说,Binding 对象要求您指定该位置,例如,“文本框” 的 “文本” 属性绑定到某个表的
LastName 属性。但实际上会显示哪个记录呢?谁来管理在整个数据源中的导航?这正是绑定管理器对象大显身手的地方。
窗体的绑定上下文
每个窗体将所有子控件定义的绑定集中到一个名为 BindingContext 的集合。您可以将窗体的绑定上下文视为声明为子控件的所有
DataBinding 集合的总和。正如前面所提到的,每个绑定引用一个具体的数据源,您可以按数据源查询 BindingContext。下面的代码行选择了
涉及指定的数据源以及来自窗体的绑定上下文的数据成员的所有绑定。
m_bmbEmployees = Me.BindingContext(m_ds, "Employees")
不过,BindingContext 集合不返回行集合或 Binding 对象集合。它返回的却是一种超级对象(绑定管理器),它管理各个控件在指定的数据
源上设置的所有数据绑定。
绑定管理器是一个从 BindingManagerBase 继承的类的实例。它代表绑定的控件操作一个特定数据源,并支持数据检索和索引维护。只要数据
源对列表中的对象求值,绑定管理器就维护当前位置,还通过激发事件来通知该数据源上的当前位置已更改。
绑定管理器类的关键属性是 Position 和 Current。Position 是一个从 0 开始的整数值,它指示当前记录在数据源中的序号位置。Current
则返回在当前位置发现的数据对象。当应用程序(或数据绑定控件)修改当前位置或更改当前数据对象中的任何字段时,绑定管理器类激发两
个相关事件,它们是 PositionChanged 和 CurrentChanged,修改触发。我不打算在这里讨论这些事件,但如果您希望详细了解它们的用处,
请参阅我在 MSDN Magazine 中发表的专栏文章 February 2002 Cutting Edge column。
在上面的示例应用程序中,您可能已经注意到两个用于在记录之间来回移动的按钮。下面是实现该功能的基本代码示例:
在显示为另一个更为用户友好的格式。
绑定到 BLOB 字段
Windows 窗体数据绑定很适用于 BLOB 字段(即图像),但并非以默认方式来实现。如果您尝试将 Employees.Photo 列绑定到PictureBox 的
Image 属性,会引发一个异常。
Dim b5 As Binding
b5 = New Binding("Image", m_ds, "Employees.Photo")
hired.DataBindings.Add(b5)
产生该异常的原因是所需的类型 (System.Drawing.Image) 和解析为 System.Byte[] 的 Photo 字段的内容之间明显不兼容。使情况更为复杂
的是,Northwind 的 Employees 图片还需要进行某种处理才能真正使用。
所有这些问题最后归结为,您需要的不仅仅是到成功绑定到图像的简单转换。尽管如此,您还是可以利用 Format 事件轻松完成任务。
如果您使用 C#,就不必声明全局数据成员来捕获事件。因此,您的代码可以显示如下:
Binding b5;
b5 = new Binding("Image", m_ds, "Employees.Photo");
b5.Format += new ConvertEventHandler(this.PictureFormat);
Photo.DataBindings.Add(b5);
事件处理程序将字节数组转换为 Bitmap 对象,该对象可以安全地分配给 PictureBox 控件的 Image 属性。
void PictureFormat(Object sender, ConvertEventArgs e)
{
// e.Value is the original value
Byte[] img = (Byte[]) e.Value;
// Perform the conversion
// (78 is the offset to skip ONLY FOR images in NorthWind)
MemoryStream ms = new MemoryStream();
int offset = 78; // should be 0
ms.Write(img, offset, img.Length - offset);
Bitmap bmp = new Bitmap(ms);
ms.Close();
// Writes the new value back
e.Value = bmp;
}
从 SQL Server™ 表读取的字节数组首先复制到 MemoryStream 对象。此步骤是必不可少的,因为,在 .NET 中,您不能直接从字节数组创建图
形对象。取而代之的是,将这些字节包装到流对象可满足 Bitmap 类的至少一个构造函数的期望。通常,数据库 BLOB 字段只包含图像本身。
但是,Northwind 却不是这样,它的情况是,图像以 78 字节的标题为前缀。因此,为了创建一个有效的对象,您必须跳过那些字节,并将摘
录传递到 Bitmap 的构造函数。一般而言,上述过程很实际地说明了在绑定数据之前如何执行任何种类的任务。当达到源类型和目标类型之间
的合理匹配后,您可以替换 Value 属性的当前内容。之后,该方法返回。
返回页首
对话栏:ADO.NET 和 ExtendedProperties
我发现许多 ADO.NET 控件都包含一个名为 ExtendedProperties 的集合属性。您能否简单描述一些我可以在其中利用这种属性的方案?
您可以使用 ExtendedProperties 属性将自定义信息添加到公开它的任何对象,包括 DataTable、DataSet 和 DataColumn。您可以将它视为一
般的 cargo 变量,类似于许多 ActiveX® 控件的 Tag 属性。您添加名称/值对,并使用熟悉而典型的集合编程接口来管理内容。例如,您可以
使用数据集 ExtendedProperties 集合的存储槽来存储应该用于刷新数据集本身的 SQL 命令。
这组扩展属性连同构成某个 ADO.NET 对象的基本信息被序列化为 XML。序列化过程使用名为 msprop:XXX 的特殊标记属性来添加这些属性,其
中,XXX 是属性名。
虽然 ExtendedProperties 集合可以接受任何类型的值,但如果您存储除字符串以外的内容,就可能遇到麻烦。当该对象序列化时,任何扩展
属性均序列化为字符串。具体地说,该字符串就是该对象的 ToString 方法返回的内容。当数据集反序列化时,这种情况可能会引起问题。实
际上,并非所有类型都能毫无痕迹地从字符串成功重新生成。例如,Color 类。如果您对某个 Color 对象(例如,Blue)调用 ToString,您
将得到 Color [Blue]。然而,Color 类上的任何构造函数都无法从这样的字符串重新生成一个有效的对象。出于这个原因,请特别注意您在
ExtendedProperties 集合中输入的非字符串类型。