Visual Basic .NET 中的数据绑定


原文地址:http://www.microsoft.com/china/msdn/library/langtool/vbnet/DataBindinginVBNET.mspx

下载本文的代码: AdvancedBasics0308.exe (223KB)

问 在 Visual Basic .NET 中编写代码时,如何充分利用应用程序中的数据绑定?

答 正如我在 Advanced Basics 上个月的专栏中提到的那样,数据绑定在 Microsoft .NET 框架中仍然存在并且能够很好地工作,这个伟大的技术使数据的处理在许多类型的应用程序中更有用。 让我们接着上期专栏的结尾来看一些实际的数据绑定应用程序。

让我们首先讨论一下结构。 图 1 概述了结合使用数据绑定和数据源的常见方法。 通过数据库从数据源提取数据,并将它放到非类型化 DataSet 中。 之所以使用非类型化 DataSet,是为了允许使用通用数据访问库。 这允许您调用可返回任何 DataSet 的单个函数。 现在可以提取这个非类型化 DataSet,并通过使用两个匹配表来将它与类型化 DataSet 合并。 最后,可以将控件绑定到类型化 DataSet。


1 绑定

您可能想知道为什么不直接绑定到非类型化 DataSet。 这个问题问得很好,因为类型化 DataSet 可提供诸如字段早期绑定之类的功能。 这允许您将 .CustomerName 用作 DataSet 的成员(而不是用值 "CustomerName" 引用 Items 集合),从而大大简化数据的访问过程。 这种早期绑定还允许您在设计时提供对数据绑定的自动支持。 这使您能够在编辑器中选择字段,甚至在某些控件中访问自定义属性。

让我们看一下现实中的数据绑定。 请查看下面的函数,该函数摘自我于 2003 年 7 月撰写的专栏,它从 SQL Server™ 提取数据并返回一个 DataSet:

Function RetrieveCustomerContacts() As DataSet
Dim ds As DataSet
Try
ds = RunSQLWithDataSet("Select CustomerID,  " & _
"CompanyName, ContactName, NoOfCustomerVisits " & _
"from customers", ConnectionString, "Customers")
Catch ex As Exception
ds = Nothing
End Try
Return ds
End Function

接着,考虑一个名为 Customers 且具有下列字段的类型化 DataSet,我已经将这些字段作为 dsCustomers.xsd(为了节省空间,我忽略了部分 XSD)添加到项目中:

      <xs:sequence>
<xs:element name="CustomerID" type="xs:string" />
<xs:element name="CompanyName" type="xs:string" />
<xs:element name="ContactName" type="xs:string"
minOccurs="0" />
<xs:element name="NoOfCustomerVisits" type="xs:int"
minOccurs="0" />
</xs:sequence>

在 Visual Studio .NET 中,可通过以下方法将此数据绑定到 DataSet:将 DataSet 组件从工具箱拖放到您的窗体上,然后将它与名为 Customers 的类型化 DataSet 进行关联。 现在,可使用 DataGrid 控件的属性窗口,可以将新的 DataSet 选作 DataSource,将 Customers 表选作 DataMember。

此过程可通过在窗体的 Load 事件中添加以下代码来完成:

   Dim ds As DataSet
Dim oClass As New SomeComponent.Class1
Try
ds = oClass.RetrieveCustomerContacts
dsCustomers.Merge(ds)
Catch ex As Exception
sbarmain.Text = ex.Message
End Try

上面的代码调用 RetrieveCustomerContacts 类以检索 DataSet,并将检索到的 DataSet 合并到窗体的类型化 DataSet 中。 当然,使用其他控件(例如,列表框和组合框控件),也可以执行这种类型的数据绑定。 还有一个有用的绝佳功能。 使用我刚提到的同样代码,可以将类型化 DataSet 加载到您的组合框中。 然后,只需使用 SelectedIndexChanged 事件中的一行代码即可提取所选值:

txtSelectedID.Text = ComboBox1.SelectedValue

当然,如果要将 txtSelectedID 控件绑定到属性名为 CustomerID 的类或结构,则可以进行这样的绑定,而不将数据直接放到该控件中:

CustomerContacts.CustomerID = ComboBox1.SelectedValue

这将更新该类的 CustomerID 字段,而且数据绑定将更新绑定文本框中显示的值。

让我们了解另一种数据绑定方法,该方法使得由数据驱动的应用程序对于用户来说更友好。 例如,假设您的应用程序必须在三个文本框中显示 DataSet 或其他可绑定对象的数据。 CurrencyManager 可用于在这三个控件之间导航并提供其他功能。 我看过几个使用 CurrencyManager 的示例,它们相当笨拙,因此我借用 MSDN 库中某个示例的概念,并将其变得更实际。 图 2 显示此窗体的界面。


2 Currency Manager 界面

此窗体允许您浏览 DataSet 并在浏览时对数据进行改变。 每次对值进行更改并移到下一条记录时,所做的更改会存储到 DataSet 中, 这与数据绑定在 Visual Basic 6.0(和更低版本)中的工作方式几乎相同。 现在,Visual Basic .NET 使其更加灵活,因为您可以访问数据绑定的低级功能。

让我们看一下图 3 中的代码,以便让我们分享我所学到的一些东西。 代码相当简单,但是需要一点解释。 我将忽略例行代码部分(例如,从 SQL Server 提取数据并返回它的类),但是,本专栏的下载文件中包含整个代码。

  1Private WithEvents thisCurrencyManager As CurrencyManager
  2
  3'Variable defs go here
  4
  5'Form Load builds the dataset, merges it with CustomerInfo1, 
  6'then binds the controls by calling BindControls
  7
  8Private Sub BindControls(ByVal thisDataTable As DataTable)
  9' Bind a TextBox control to a DataTable column 
 10'in a DataSet.
 11txtCompany.DataBindings.Add("Text", thisDataTable, "CompanyName")
 12txtName.DataBindings.Add("Text", thisDataTable, "ContactName")
 13txtCity.DataBindings.Add("Text", thisDataTable, "City")
 14txtCustomerID.DataBindings.Add("Text", thisDataTable, "CustomerID")
 15
 16' Specify the CurrencyManager for the DataTable.
 17thisCurrencyManager = _
 18    CType(Me.BindingContext(thisDataTable), CurrencyManager)
 19' Set the initial Position of the control.
 20thisCurrencyManager.Position = 0
 21End Sub

 22
 23Private Sub MoveNext(ByVal thisCurrencyManager As CurrencyManager)
 24
 25If thisCurrencyManager.Position = thisCurrencyManager.Count - 1 Then
 26MessageBox.Show("You're at end of the records")
 27Else
 28thisCurrencyManager.Position += 1
 29CheckChanges()
 30End If
 31End Sub

 32
 33Private Sub MoveFirst(ByVal thisCurrencyManager As CurrencyManager)
 34
 35thisCurrencyManager.Position = 0
 36CheckChanges()
 37End Sub

 38
 39Private Sub MovePrevious(ByVal thisCurrencyManager As CurrencyManager)
 40
 41If thisCurrencyManager.Position = 0 Then
 42MessageBox.Show( _
 43    "You're at the beginning of the records.")
 44Else
 45thisCurrencyManager.Position -= 1
 46CheckChanges()
 47End If
 48End Sub

 49
 50Private Sub MoveLast(ByVal thisCurrencyManager As CurrencyManager)
 51
 52thisCurrencyManager.Position = thisCurrencyManager.Count - 1
 53CheckChanges()
 54End Sub

 55
 56'Button click events go here
 57
 58Private Sub txtName_TextChanged(ByVal sender As System.Object, _
 59    ByVal e As System.EventArgs) Handles txtName.TextChanged
 60'Exit if starting up
 61If IsNothing(thisCurrencyManager) Then
 62Exit Sub
 63End If
 64
 65StartEditMode()
 66
 67End Sub

 68
 69Private Sub txtCompany_TextChanged(ByVal sender As _
 70    System.Object,ByVal e As System.EventArgs) _
 71    Handles txtCompany.TextChanged
 72StartEditMode()
 73End Sub

 74
 75Private Sub txtCity_TextChanged(ByVal sender As _
 76    System.Object, ByVal e As System.EventArgs) _
 77    Handles txtCity.TextChanged
 78StartEditMode()
 79End Sub

 80
 81Sub StartEditMode()
 82cmdSaveChanges.Enabled = True
 83End Sub

 84
 85Sub EndEditMode()
 86Me.BindingContext(CustomerInfo1.Customers).EndCurrentEdit()
 87End Sub

 88
 89Private Sub thisCurrencyManager_PositionChanged( _
 90    ByVal sender As Object, _
 91    ByVal e As System.EventArgs) _
 92    Handles thisCurrencyManager.PositionChanged
 93cmdSaveChanges.Enabled = False
 94End Sub

 95
 96Private Sub cmdNew_Click(ByVal sender As System.Object, _
 97    ByVal e As System.EventArgs) Handles cmdNew.Click
 98thisCurrencyManager.AddNew()
 99End Sub

100
101'Other subs go here

图3  数据绑定的关键代码元素

图 3 中的第一行代码为我的应用程序定义 CurrencyManager。 它用 WithEvents 进行定义,以便允许我在需要时使用它的事件:

Private WithEvents thisCurrencyManager As CurrencyManager

frmBinder2_Load 事件中的代码相当标准。 它调用一个可返回 DataSet 的方法,然后将返回的 DataSet 合并到类型化 DataSet 中。 类型化 DataSet 的 Customers 表传递到可执行绑定的 BindControls 方法。

BindControls 方法就是 CurrencyManager 在其中操作的方法。 前几行代码将该 DataSet 中的四个字段绑定到控件的 Text 属性:

txtCompany.DataBindings.Add("Text", thisDataTable, "CompanyName")
txtName.DataBindings.Add("Text", thisDataTable, "ContactName")
txtCity.DataBindings.Add("Text", thisDataTable, "City")
txtCustomerID.DataBindings.Add("Text", thisDataTable, "CustomerID")

实际上,BindControls 的最后两行代码使用 CurrencyManager。 倒数第二行将 CurrencyManager 设置为数据源(在本例中为 DataTable)的 BindingContext:

thisCurrencyManager = CType(Me.BindingContext(thisDataTable), _
CurrencyManager)

最后一行将初始位置设置为 0(第一条记录):

thisCurrencyManager.Position = 0

现在,我可以继续使用 CurrencyManager。 所有的 Move 方法都通过使用 CurrencyManager 来改变记录在 DataSet 中的位置。 例如,只要当前的位置不是最后一个元素,MoveNext 方法就在数据源中向前移动当前的元素指针:

If thisCurrencyManager.Position = _
thisCurrencyManager.Count - 1 Then
MessageBox.Show("You're at end of the records")
Else
thisCurrencyManager.Position += 1
CheckChanges()
End If

在位置每次发生改变时,都调用 CheckChanges 方法,以便确定 DataSet 是否也发生了更改。

我在构建这个示例应用程序时,被一个小问题困扰了几个小时。 我可以很好地滚动记录,并且当我将位置改变到另一行时,对上一行的更改生效。 但是,当我使用 Save 按钮保存所做的更改时,GetChanges 方法无法正确地指出已发生更改。 我按照 MSDN 文档和联机示例检查代码,在进行了一些探查之后,我找到了问题所在。

我将 DataTable 用作了数据源。 EndEditMode 过程用下面的代码行调用了 EndCurrentEdit 方法:

Me.BindingContext(CustomerInfo1, "Customers").EndCurrentEdit()

该代码不工作;即使数据确实发生了更改,DataSet 也显示未进行更改。 最后,我认识到所使用的上下文有误。 下面的上下文才能够正常工作:

Me.BindingContext(CustomerInfo1.Customers).EndCurrentEdit()

在本例中,我只传递 DataTable,而 DataTable 的确是这些控件的数据源。 可笑的是,最初的代码在执行过程中不产生任何错误,而只是不工作。 我之所以确信这行代码的确能够执行,是因为当我将表名 Customers 改为 CustomersX 时,这行代码生成了一个运行时错误。 正如我说过的那样,该代码能够执行,只不过它指向了错误的数据源。

此窗体中的其他代码相当简单,因为它们实现对 move 方法的调用并且处理与 CurrencyManager 的交互。

一定要注意 cmdAddNew_Click 事件,因为它导致在数据中添加一个新行。 这会刷新窗体并允许您在新行中输入数据。 通过离开和回到该行可以看到自动保存了更改。

thisCurrencyManager_PositionChanged 事件会在位置发生改变时引发。 此事件用于重置 SaveChanges 按钮的状态。

有关数据绑定的详细信息,请参阅 Billy Hollis 的 "Not Your Father's Data Binding" 一文。 有关数据绑定的更多信息,请参阅 MSDN Magazine 中的 Data Binding

请将您提给 Ken 的问题和意见发送至 basics@microsoft.com

Ken Spencer 为 32X Tech (http://www.32X.com) 工作,他就 Microsoft 技术提供培训、软件开发和咨询服务。

来自 2003 年 8 月发行的 MSDN Magazine

可以从当地报摊或最好通过订阅购买该杂志。

转到原英文页面

posted @ 2006-04-25 12:11  ahwey  阅读(572)  评论(0)    收藏  举报