本章的代码示例是从应用示例 DataTree.vbp which is listed in the Samples directory中得到的。
可将数据库中的数据绑定到 TreeView 控件。下面的示例将 TreeView 控件绑定到 Biblio 数据库,该数据库可以在 Visual Basic CD 中找到。该应用实例将 Publishers 表作为树节点的第一层。如果一个出版商对应于一个或多个书名,则这些书名将作为该出版商的子节点加入树中。
图 2.42 与数据绑定的 TreeVew 控件
下面的代码用到了如下对象:
将 Biblio.mdb 数据库绑定到 TreeView 控件
要将数据库绑定到 TreeView 控件,必须先添加对当前版本的数据访问对象(DAO)的引用。
由于需要在一个会话中多次访问 Biblio.mdb 数据库,如果创建一个模块级的 Database 对象,保持一个打开数据库,将有助于提高效率。此后,不需要打开数据库即可访问它。在窗体的声明部分,键入如下内容:
Private mDbBiblio As Database
如果希望该数据库还可被其它模块使用,可以用 Public 语句,并重命名该变量,以表明它是全局的,例如 gDbBiblio
。
在创建 Node 对象时,在 Set 语句中(如下所示)使用 Node 类型的变量。
Dim TempNode As Node
Set TempNode = tvwDB.Nodes.Add()
虽然可以在添加 Node 对象时创建变量,更有效的方式是声明一个模块级的 Node 对象变量,并用它创建所有的 Node 对象。在上述声明部分再键入:
Private mNode As Node
Form 对象的 Load 事件中可以初始化 Database 变量。代码如下:
Set mDbBiblio = DBEngine.OpenDatabase("BIBLIO.MDB")
在成功地初始化 Database 对象变量后,就可以在该模块的代码中的任何位置自由地访问它了。
至此,Database 对象变量已经被初始化为 Biblio 数据库,现在可以创建树中的第一个节点,并将打开的数据库的名称赋予它。首先必须用 Node 集合的 Add 方法创建第一个 Node 对象。还要使用 Set 语句将其赋给 mNode 对象变量,如下所示:
Set mNode = tvwDB.Nodes.Add() '
创建第一个节点。mNode.Text = mDbBiblio.Name
注意,在上面的代码中,在创建 Node 的同时用 Set 语句将其赋给了 mNode 对象变量。由于 mNode 变量现在包含了新创建的 Node 对象,可以对该 Node 对象的属性进行赋值。在上述情况下,Database 的名称(即 Database 对象的 Name 属性)已经被赋给了新节点的 Text 属性。
本应用实例假定存在名为“cmdLoad”的按钮,并且当用户单击它时,置入 Biblio 数据库中的两个表到 TreeView 控件中。为此,必须首先在该按钮的 Click 事件中声明两个 DAO 对象变量。第一个变量 rsPublishers 用来包含 Publishers 表。第二个变量 rsTitles 用来包含 Titles 表。下面的代码声明了这两个变量,并用 OpenRecordSet 方法将表赋给变量:
Dim rsPublishers As Recordset
Dim rsTitles As Recordset
Set rsPublishers = mDbBiblio. _
OpenRecordset("Publishers", dbOpenDynaset)
Set rsTitles = mDbBiblio. _
OpenRecordset("titles", dbOpenDynaset)
现在有两个打开的记录集,可以遍历每个记录集,创建 Node 对象,并为该对象的 Text 属性赋予合适的值。首先,必须遍历 Publishers 表,并为该表中的每个出版商创建一个 Node 对象。
下列简化了的代码可以用一句话概括为,“逐个处理每个记录,直到记录集的末尾:创建 Node 变量,并将 Title 字段的值赋给其 Text 属性,移到下一记录并重复”:
Do Until rsPublishers.EOF
Set mNode = tvwDB.Nodes.Add(1, tvwChild)
mNode.Text = rsPublishers!Name
rsPublishers.MoveNext
Loop
注意,在上面的 Add 方法中用了两个参数。第一个参数(1)是我们希望添加入节点的 Node 的 Index 属性。也就是说,希望所有的出版商节点成为第一个(根)节点(在 Form 的 Load 事件中创建的)的子节点。第二个参数使用了常数 (tvwChild),该常数指定新的 Node 将成为编号为“1”的 Node 的子节点。
上面的代码将 Publishers 表的内容作为第一层填入 TreeView 中。然而,我们还希望能够进入更深一层,为每个出版商节点增加子节点。每个子节点代表该出版商印刷的一本书。
为了做到这一点,如果有了对新创建的出版商节点 (mNode) 的引用,只要遍历 Titles 记录集,并检查每条记录的 PubID 字段即可。如果该字段与 Publishers 记录集中的 PubID 字段相匹配,则该书是由当前的出版商出版的。但是,在能够为 mNode 添加节点之前,还必须先将 mNode 的 Index 属性赋给一个变量 (intIndex),如下所示:
intIndex = mNode.Index
然后就可以在 Add 方法中使用该变量了,Add 方法需要用来加入子节点的 Node 对象的 Index 属性:
Set mNode = tvwDB.Nodes.Add(intIndex, tvwChild)
如下简化的代码可被表述为“直到 Recordset 的结尾:创建子 Node 对象,并将 Title 字段的值赋给它的 Text 属性;移动到下一记录并重复上述操作”:
Do Until rsTitles.EOF If rsPublishers!PubID = rsTitles!PubID Then Set mNode = tvwDB.Nodes.Add(intIndex, tvwChild) mNode.Text = rsTitles!Title 'Text
属性。End If
Loop
上面的代码显示了用两个相关的表填成一个表的基本策略。全部代码如下:
'
必须设置对DAO 3.5
的引用。'
在声明部分,声明模块级的对象变量:Private mDbBiblio As Database
Private mNode As Node
Private Sub Form_Load()
'
在Form_Load
事件中,设置对象变量,'
并创建TreeView
控件的第一个Node
对象。Set mDbBiblio = DBEngine.Workspaces(0). _
OpenDatabase("BIBLIO.MDB")
tvwDB.Sorted = True
Set mNode = tvwDB.Nodes.Add()
mNode.Text = "Publishers"
mNode.Tag = mDbBiblio.Name '
设置Tag
属性。mNode.Image = "closed" '
设置Image
'
属性End Sub
Private Sub cmdLoad_Click()
'
声明DAO
对象变量,'
并将记录集赋予它们。Dim rsPublishers As Recordset
Dim rsTitles As Recordset
Set rsPublishers = mDbBiblio. _
OpenRecordset("Publishers", dbOpenDynaset)
Set rsTitles = mDbBiblio. _
OpenRecordset("titles", dbOpenDynaset)
'
移到第一条记录。rsPublishers.MoveFirst
Dim intIndex As Integer '
用于索引的变量。'
直到最后一条记录(EOF)
:添加一个Node
对象,'
并用Name
字段作为新'Node
对象的文本。Do Until rsPublishers.EOF
Set mNode = tvwDB.Nodes.Add(1, tvwChild)
mNode.Text = rsPublishers!Name
mNode.Tag = "Publisher" 'Tag
标识该表。'
为Key
赋予唯一的ID
mNode.Key = CInt(rsPublishers!PubID) & " ID"
'
将变量intIndex
设置为新创建Node
的Index
属性。'
用该变量将子Node
对象加入当前Node
。intIndex = mNode.Index
'
对这条记录,在Title
表中查找与Titles
记录集'
中出现相同PubID
的记录。如果找到这样的记录,'
则在TreeView
控件中加入Node
对象,并将用所'
找到记录的Title
、ISBN
和Author
字段为新'Node
对象的属性赋值。Do Until rsTitles.EOF
If rsPublishers!PubID = rsTitles!PubID Then
Set mNode = tvwDB.Nodes. _
Add(intIndex, tvwChild)
mNode.Text = rsTitles!Title '
文本。mNode.Key = rsTitles!ISBN '
唯一的ID
。mNode.Tag = "Authors" '
表名。mNode.Image = "leaf" '
图象。End If
rsTitles.MoveNext 'Titles
中的下一条记录。
Loop
'
将rsTitles
重新设置为Titles
的第一条记录。rsTitles.MoveFirst
'
移动到下一条Publisher
记录。rsPublishers.MoveNext
Loop
End Sub
使用 SQL 语句创建较小的“Titles”记录集,可以对这个示例加以改进。下面的代码创建只包含相同 PubID 值的记录的记录集:
Set rsTitles = mDbBiblio.OpenRecordset _
("select * from Titles Where PubID = " & _
rsPublishers!PubID)
然后该代码就只需对较小的记录集进行循环,因而效率比较高。修改后的代码如下:
Private Sub cmdLoad_Click() Dim rsPublishers As Recordset Dim rsTitles As Recordset Set rsPublishers = mDbBiblio. _ OpenRecordset("Publishers", dbOpenDynaset) Dim intIndex Do Until rsPublishers.EOF Set mNode = tvwDB.Nodes.Add(1, tvwChild) mNode.Text = rsPublishers!Name mNode.Tag = "Publisher" '
标识表。mNode.Key = rsPublishers!PubID & " ID"
mNode.Image = "closed"
intIndex = mNode.Index
'
对这条记录,使用查询创建Title
表的记录集,'
查询条件是所有包含相同PubID
的记录。对结果记录集中'
的每一条记录,在TreeView
控件中加入一个Node
对象,'
并用记录的Title
、ISBN
和Author
字段为新'Node
对象的属性赋值。Set rsTitles = mDbBiblio.OpenRecordset _
("select * from Titles Where PubID = " & _
rsPublishers!PubID)
Do Until rsTitles.EOF
Set mNode = tvwDB.Nodes. _
Add(intIndex, tvwChild)
mNode.Text = rsTitles!TITLE '
文本。mNode.Key = rsTitles!ISBN '
唯一的ID
。mNode.Tag = "Authors" '
表名。mNode.Image = "smlBook" '
图象。'
移动到rsTitles
中的下一个记录。rsTitles.MoveNext
Loop
'
移动到下一个Publishers
记录。rsPublishers.MoveNext
Loop
End Sub