VB.Net中层次数据绑定TreeView的实现
目录:
1、 从层次数据的表达方式开始
2、 继承自TreeNode的myTreeNode
3、 将dbTreeView绑定到数据源
4、 实现数据与绑定控件的同步
5、 使用dbTreeView
1、从层次数据的表达方式开始
在本例中,部门表(department)中有五个字段,如下表:
字段名 | 字段类型 | 说明 |
ID | 自动编号 | Key |
Code | String | 编码 |
Name | String | 名称 |
PID | Int | 父结点的ID |
CPtr | boolean | 是否有子结点 |
2、继承自TreeNode的myTreeNode
在myTreeNode中,新增了三个属性,如下表:
属性名 | 类型 | 说明 |
Value | Object | Key |
PID | Object | 父结点的ID |
CPtr | Boolean | 是否有子结点 |
在Init事件中,根据传入的四个参数,设置这三个属性和Text属性。
3、将dbTreeView绑定到数据源
属性名 | 类型 | 说明 |
Datasource | dataview | dbTreeVIew的数据源使用dataview,而不是object |
ValueMember | string | 值成员(数据源[dataview]的列名) |
DisplayMember | string | 显示(在Text中)成员 |
PidMember | string | 父ID成员 |
CPtrMember | string | 是否有子结点 |
后四个属性对应myTreeNode的value,text,pid,cptr。
相关代码如下:< Category( " Data " ) > Protected Property DataSource() As Object
Get
Return mDataView
End Get
Set(ByVal Value As Object)
If Value Is Nothing Then
Else
mDataView = Value
cm = CType(Me.BindingContext(mDataView), CurrencyManager)
UpdateTreeView()
End If
End Set
End Property
< Category( " Data " ) > Protected Property PidMember() As String
Get
Return mPidMember
End Get
Set(ByVal Value As String)
mPidMember = Value
End Set
End Property
< Category( " Data " ) > Protected Property DisplayMember() As String
Get
Return Join(mDisplayMember, SplitChar)
End Get
Set(ByVal Value As String)
mDisplayMember = Split(Value, SplitChar)
End Set
End Property
' 注意,这几个属性都是保护成员,必须在Init事件中设置:
Public Sub Init(ByVal dispmember As String, ByVal valuemember As String, ByVal pidmember As String, ByVal cptrmember As String, ByVal datasource As DataView)
Me.ValueMember = valuemember
Me.DisplayMember = dispmember
Me.PidMember = pidmember
Me.CPtrMember = cptrmember
Me.DataSource = datasource
' 取value最大值,新增时将value+1,保证关健值唯一。
Me.mDataView.Sort = Me.ValueMember
Me.m_MaxID = Me.GetValue(Me.mDataView.Count - 1 )
End Sub

Protected Overridable Function GetDisplay(ByVal Index As Integer) As Object
Dim i As Integer
Dim temp As String = ""
For i = 0 To mDisplayMember.Length - 1
temp = temp & IIf(i > 0 , LinkChar, "" ) & mDataView(Index)(mDisplayMember(i))
Next
Return temp
End Function
其它检索值的函数请参见源程序。
UpdateTreeView调用私有方法FillTree来生成树,需要注意的,FillTree只是生成指定结点的子结点并将其添加到指定结点,而不是一次就将所有结点添加到树中,如果未指定结点(第一次填充时),只是添加顶层结点。
Private Sub FillTree(ByRef pnode As myTreeNode, Optional ByVal filter As String = "" )
mDataView.RowFilter = filter
Dim i As Integer, icol As Integer
Dim newnode As myTreeNode
RemoveHandler cm.PositionChanged, AddressOf cm_PositionChanged
Me.BeginUpdate()
For i = 0 To mDataView.Count() - 1
newnode = New myTreeNode(GetDisplay(i), GetValue(i), GetPid(i), GetCPtr(i))
' 当有子结点时,为这个结点添加一个空子结点
If newnode.CPtr Then
Dim nullnode As New myTreeNode()
nullnode.Value = NoExpandNodeValue
newnode.Nodes.Add(nullnode)
End If
If pnode Is Nothing Then
Me.Nodes.Clear()
Me.Nodes.Add(newnode)
Else
pnode.Nodes.Add(newnode)
End If
Next
Me.EndUpdate()
mDataView.RowFilter = ""
AddHandler cm.PositionChanged, AddressOf cm_PositionChanged
End Sub
在展开有子结点的结点前,删除所有子结点,再用FillTree为待展开结点新增子结点。
Private Sub dbTreeView_BeforeExpand(ByVal sender As Object, ByVal e As System.Windows.Forms.TreeViewCancelEventArgs) Handles MyBase.BeforeExpand
' 当是新增结点引起BeforeExpand事件时,直接退出。
If ExpandWhenAddNode Then Exit Sub
' 在展开结点前更新子结点
Dim currentnode As myTreeNode = CType(e.Node, myTreeNode)
With currentnode
.Nodes.Clear()
FillTree(currentnode, mPidMember & " = " & CInt(.Value))
End With
End Sub
4、实现数据与绑定控件的同步
1 、 其它绑定控件(如textbox等)应与TreeView当前结点所指向的记录位置一致。
Private Sub dbTreeView_AfterSelect(ByVal sender As Object, ByVal e As System.Windows.Forms.TreeViewEventArgs) Handles MyBase.AfterSelect
If e.Node Is Nothing Then Exit Sub
' 定位到position
cm.Position = GetPosition(CType(e.Node, myTreeNode).Value)
If AllowEdit Then
oldNode = e.Node
oldPos = cm.Position
End If
End Sub
2 、在其它绑定控件改变了数据源后,更新树结点,这个工作在触发CurrencyManager的PositionChanged事件时进行。
Public Sub cm_PositionChanged(ByVal sender As Object, ByVal e As System.EventArgs)
If CType(Me.SelectedNode, myTreeNode).Value <> GetValue(cm.Position) Then
Debug.WriteLine( " Current node isn't correct point to currencymanager.position! " )
Me.SelectedNode = FindNodeByValue(GetValue(cm.Position), Me.Nodes)
End If
If AllowEdit Then
If Me.SelectedNode Is Nothing AndAlso cm.Position = cm.Count - 1 Then
' 当新增记录时,新增树结点
If CType(cm.Current, DataRowView).IsNew Then
Me.SelectedNode = AddNode(cm.Position)
Exit Sub
End If
End If
If Not oldNode Is Nothing Then
If CType(oldNode, myTreeNode).Value = GetValue(oldPos) Then
' 更新老结点
oldNode.Text = GetDisplay(oldPos)
Else
End If
End If
End If
End Sub
使用dbTreeView
程序运行后界面如下:
相关代码请参见源程序,这里不做详述,需要注意的是删除操作并没有删除子结点,只是删除当前结点而已,删除子结点的工作应该在存储过程中递归实现,而不应放在前端。