提要:在VB6中,常將TreeView用來表示層次數據,但相關的與數據庫進行交互的代碼,需要大量的采用手工編碼;在VB.net中,由於數據綁定功能的加強及語言特性的增強,可以很容易的實現TreeView與層次數據的綁定,本文將首先建立一個繼承自TreeView的 dbTreeView,然後用一個單位(部門)的層次數據與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 Value Member string值成員(數據源[dataview]的列名) Display Member string顯示(在Text中)成員 Pid Member string父ID成員 CPtr Member string是否有子結點
後四個屬性對應myTreeNode的value,text,pid,cptr。
相關代碼如下:
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
Protected Property PidMember() As String
Get
Return mPidMember
End Get
Set(ByVal Value As String)
mPidMember = Value
End Set
End Property
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
設置DisplayMember屬性的格式如:字段1;字段2;字段3…
在設置屬性時,將傳來的參數轉換為字符串數組mDisplayMember,在檢索值時返回數據如:值1 值2 值3.…
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
程序運行後界面如下:
相關代碼請參見源程序,這裡不做詳述,
需要注意的是刪除操作並沒有刪除子結點,只是刪除當前結點而已,刪除子結點的工作應該在存儲過程中遞歸實現,而不應放在前端。