当在程序中需要显示多条记录时,我们往往希望能给记录一个“记录号”以方便定位记录,比如:“到第一条”,“到最后一条”,“跳到第100条”……。
DataTable中的记录(即DataRow)本身并没有一个所谓的“记录号”,DataTable本身也没有提供这种功能。
提示:
在VB6中,记录集对象RecordSet提供MoveFirst,MoveLast等定位功能。这是由ADO实现的。在ADO.NET中,DataTable和DataRow都没有这些方法。
乍看起来,不能在数据集中移动,好象是ADO.NET设计的一个疏漏。事实上,ADO.NET把所有在数据集中定位的功能全部抽取出来,再加上其他一些功能,构建了ADO.NET的数据绑定机制,与原来ADO所提供的有限定位功能相比,ADO.NET更为强大而灵活。
请试着运行本书配套光盘中的示例MoveInDataTable,其运行界面如图 7 15:
图 7 15 数据绑定示例
这个程序在运行时,既可以点击右下方的四个按钮,也可以直接点击上部的网格在数据集中移动,当前行的内容同步在下半部的两个文本框中显示。可以在文本框中直接修改数据,当移动到其它行时,网格中的记录同步刷新。
通过这个示例,我们来剖析ADO.NET中数据绑定机制的原理。
所谓数据绑定,通俗地说,就是把数据源(如DataTable)中的数据取出来,显示在窗体的各种控件上,用户可以通过这些控件查看和修改数据,这些修改会自动地保存到数据源中,参见图 7 16。
图 7 16 数据绑定原理
Windows 窗体可以利用两种类型的数据绑定:简单绑定和复杂绑定。这两种类型具有不同的优点。
l 简单绑定
简单数据绑定指将一个控件绑定到单个数据元素(如数据集表的列中的值)的能力。这是用于 TextBox 控件或 Label 控件等控件(即通常只显示单个值的控件)的典型绑定类型。 图 7 15中的两个文本框,分别绑定到了DataTable中的“AddressStr”和“ClientName”两个字段。
l 复杂绑定
复杂数据绑定指将一个控件绑定到多个数据元素的能力,通常绑定到数据库中的多条记录。 图 7 15中的DataGrid就绑定到了一个DataTable,它可以一次显示多条记录和多个字段的值。
在数据集中移动是数据绑定机制中的一个子功能。
为了方便理解ADO.NET中复杂的数据绑定机制,我们先在MoveInDataTable示例中的一个简单的窗体内实现数据移动功能,参见图 7 17:
图 7 17 手动实现在记录集中移动
下面对代码进行分析
(1)程序需要提取数据,Sub过程GetData实现这一功能:
'定义相关变量
Private conn As OleDbConnection
Private comm As OleDbCommand
Private da As OleDbDataAdapter
Private ds1 As New DataSet
'获取数据
Private Sub GetData()
'创建连接对象连接数据库
conn = New OleDbConnection
conn.ConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=D:\Clients.mdb;Persist Security Info=False"
conn.Open()
'创建命令对象用于向数据库发送SQL命令
comm = New OleDbCommand
comm.CommandText = "select * from OrderClient"
comm.CommandType = CommandType.Text
comm.Connection = conn
'创建DataAdapter用于填充DataSet
da = New OleDbDataAdapter(comm)
'填充数据
da.Fill(ds1)
conn.Close()
End Sub
注意,假设数据库Clients.MDB放在“D:\”下。这些代码我们已经很熟悉了。
(2)设定数据绑定,Sub过程SetDataBing()实现这一功能:
'数据绑定管理对象
Private cm As CurrencyManager
'设置数据绑定
Private Sub SetDataBinding()
'将DataGrid绑定到DataTable
Me.DataGrid1.DataSource = ds1.Tables(0)
'获取数据绑定管理对象
cm = Me.BindingContext(ds1.Tables(0))
End Sub
在示例程序的第二窗体(参见图 7 15)中,只不过是多了将文本框绑定到DataTable中的字段中的代码:
Me.txtAddress.DataBindings.Add("Text", ds1.Tables(0), "AddressStr")
Me.txtName.DataBindings.Add("Text", ds1.Tables(0), "ClientName")
可以看到,给DataGrid设置数据绑定非常简单,只需直接设定其DataSource属性就行了。
给文本框设置数据绑定,需要向其DataBindings集合增加一项,其格式为:
"要绑定的属性名", 被绑定的DataTable对象, "DataTable中的字段名"
在上面的代码中,控件都是直接绑定到DataTable中的,事实上,也可直接绑定到DataSet,但这时需要对代码做一些改变:
'将DataGrid绑定到DataTable
Me.DataGrid1.DataSource = ds1
Me.DataGrid1.DataMember = ds1.Tables(0).TableName
'绑定文本框
Me.txtAddress.DataBindings.Add("Text", ds1, ds1.Tables(0).TableName & ".AddressStr")
Me.txtName.DataBindings.Add("Text", ds1, ds1.Tables(0).TableName & ".ClientName")
因为DataSet中可能会有多个DataTable,所以,必须给DataGrid的DataMember属性指明一个表名。在程序中动态指定某个DataTable表名,就可以动态地将DataGrid与此表名相对应的DataTable绑定。
文本框绑定的第三个参数被称为“绑定字串”,以英文句点隔开,其格式为:
表名.字段名
其特点是越左边的子字串其范围就越大。
技术内幕:
其实数据绑定不仅限于DataSet和DataTable,同样可以将文本框和DataGrid之类绑定到其它对象,如数组和ArrayList,这时,绑定字串就显得非常重要了,有兴趣的读者可以在MSDN中通过搜索“数据绑定”关键字了解如何绑定到非DataSet和DataTable数据。
(3)现在可以在记录集中移动了,这是通过给CurrencyManager的Position属性赋值实现的,下面列出了关键的代码:
'移到第一条
cm.Position = 0
'移到前一条
If cm.Position > 0 Then
cm.Position -= 1
End If
'移到下一条
If cm.Position < cm.Count - 1 Then
cm.Position += 1
End If
'移到最后一条
cm.Position = cm.Count - 1
可以看到,在数据集中移动是通过CurrencyManager对象来实现的。
展示的代码虽然简单,但如果不理解这背后所隐藏的机制,还是无法充分地利用数据绑定机制的。
ADO.NET的数据绑定机制主要由以下类构成:
图 7 18 数据绑定类
图 7 18以UML图符方式展示了ADO.NET中数据绑定的核心架构(注意,并没有完整地画出所有的类)。
提示:
UML简称为Unified Modeling Language(统一建模语言),它使用图形的方式来表达面向对象的软件系统架构,目前已被软件业广泛接受,成为了国际标准。本书第四部分介绍面向对象编程中就大量地使用UML类图表达程序结构。本书附录中有一个UML基础教程,可供读者参考学习。
现在逐个介绍每个类。
从示例中我们已经知道,象文本框这样的控件都可以绑定到字段上,这一事实本身包含以下信息:
要绑定的自身属性(如Text属性)
提供数据来源的对象(如DataTable)
绑定导航字串
…………
这些信息被封装起来,形成了Binding类。每个实现了数据绑定的控件都至少有一个Binding对象,表明它的绑定信息。
一个窗体上可能会有多个控件(比如多个文本框)绑定到一个数据源,这就意味着存在着多个Binding对象。因此需要有一个类来管理这些对象,这个类就是BindingManagerBase类,但这个类是个抽象类,不能直接创建对象,类CurrencyManager直接继承自BindingManagerBase类,实现了所有基类未实现的功能,可以直接被使用。在数据集中移动的功能就是由 CurrencyManager对象实现的。
现在知道了,要在数据集中移动,必须想法获取与此数据集对应的CurrencyManager对象。那怎样做到这点?
一个窗体上不仅会有多个控件(比如多个文本框)绑定到一个数据源,还可能出现多个控件绑定到多个数据源的情况(假设窗体中有两个DataSet,分别为两组控件提供数据,则在窗体中就会存在两个CurrencyManager对象),因此,再设计一个类用于管理这些CurrencyManager对象,这就是BindingContext类的功能。每个其上有数据绑定的窗体对象都至少会有一个BindingContext对象。
现在小结一下:
获取窗体的BindingContext对象à由BindingContext对象获取CurrencyManager对象à通过CurrencyManager对象以实现在记录集中移动的功能
这就是实现在数据集中移动功能的技术内幕。
说了这么多,其实在示例代码中只需要一句就行了:
'数据绑定管理对象
Private cm As CurrencyManager
'…………
'获取数据绑定管理对象
cm = Me.BindingContext(ds1.Tables(0))
自我探索:
一个容器控件比如Form、GroupBox和TabControl都可以有自己的BindingContext对象,以下代码设定了两个GroupBox的BindingContext对象:
Dim bcG1 As New BindingContext()
Dim bcG2 As New BindingContext()
groupBox1.BindingContext = bcG1
groupBox2.BindingContext = bcG2
请设计一个应用程序,其上有两个GroupBox,每个GroupBox中都有一些数据绑定控件,编程实现这两组控件显示同一数据源中的不同位置的数据。
从前面介绍的内容可以看到,ADO.NET提供的数据绑定功能虽然灵活,但确实过于复杂而不好用了,而在数据集中移动的功能是非常常见的,如果在每一个窗体中都重复这些代码,实在是一件枯燥无聊的工作。因此,可以把这些代码封装起来,作为一个数据绑定辅助类。DataBindingHelper类就是出于这个目的而创建的。
首先定义一些类的成员变量:
'内部绑定管理器
Private _cm As CurrencyManager = Nothing
'内部的绑定控件容器,通常是窗体
Private _container As Control = Nothing
'向外界表露CurrencyManager对象
Public ReadOnly Property cm() As CurrencyManager
Get
Return _cm
End Get
End Property
'容器控件
Public Property container() As Control
Get
Return _container
End Get
Set(ByVal Value As Control)
_container = Value
End Set
End Property
接着需要提供一个方法来指定数据源:
'数据源属性,可以指定DataMember(对于DataSet),也可省略,对于DataTable
Public Sub setDataSource(ByVal datasource As Object, Optional ByVal dataMember As String = Nothing)
If _container Is Nothing Then
MsgBox("请先指定控件容器")
Exit Sub
End If
If dataMember Is Nothing Then
_cm = _container.BindingContext(datasource)
Else
_cm = _container.BindingContext(datasource, dataMember)
End If
End Sub
现在就可以实现移动功能了,以后移一条记录为例:
Public Sub MoveNext()
'_cm未创建或为空,均退出
If _cm Is Nothing Then
Exit Sub
End If
If _cm.Count = 0 Then
Exit Sub
End If
If _cm.Position < _cm.Count - 1 Then
_cm.Position += 1
End If
End Sub
有了DataBindingHelper类,实现在数据集中移动就非常简单了,以下是示例工程中的代码:
Dim dbh1 As New DataBindingHelper
'设定容器对象为窗体
dbh1.container = Me
‘设定数据源
dbh1.setDataSource(ds1.Tables(0))
移动代码如下:
Private Sub btnMoveFirst1_Click(……) Handles btnMoveFirst1.Click
dbh1.MoveFirst()
End Sub
Private Sub btnMovePrev_Click(……) Handles btnMovePrev.Click
dbh1.MovePrev()
End Sub
是不是非常简单?
从这个小示例中相信您一定能体会到面向对象技术所带来的好处!
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=278461