提要:在VB
中
常将TreeView用来表示层次数据
但相关的与数据库进行交互的代码
需要大量的采用手工编码;在中
由于数据绑定功能的加强及语言特性的增强
可以很容易的实现TreeView与层次数据的绑定
本文将首先建立一个继承自TreeView的 dbTreeView
然后用一个单位(部门)的层次数据与dbTreeView进行数据绑定
并提供了与数据库进行交互的代码
从层次数据的表达方式开始
在本例中部门表(department)中有五个字段如下表:
字段名字段类型说明ID自动编号KeyCodeString编码NameString名称PIDInt父结点的IDCPtrboolean是否有子结点
继承自TreeNode的myTreeNode
在myTreeNode中新增了三个属性如下表:
属性名类型说明ValueObjectKeyPIDObject父结点的IDCPtrBoolean是否有子结点
在Init事件中根据传入的四个参数设置这三个属性和Text属性
将dbTreeView绑定到数据源
属性名类型说明DatasourcedataviewdbTreeVIew的数据源使用dataview而不是objectValueMemberstring值成员(数据源[dataview]的列名)DisplayMemberstring显示(在Text中)成员PidMemberstring父ID成员CPtrMemberstring是否有子结点
后四个属性对应myTreeNode的valuetextpidcptr
相关代码如下:
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(MeBindingContext(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)
MeValueMember = valuemember
MeDisplayMember = dispmember
MePidMember = pidmember
MeCPtrMember = cptrmember
MeDataSource = datasource
取value最大值新增时将value+保证关健值唯一
MemDataViewSort = MeValueMember
Mem_MaxID = MeGetValue(MemDataViewCount )
End Sub
设置DisplayMember属性的格式如:字段;字段;字段…
在设置属性时将传来的参数转换为字符串数组mDisplayMember在检索值时返回数据如:值 值 值…
Protected Overridable Function GetDisplay(ByVal Index As Integer) As Object
Dim i As Integer
Dim temp As String =
For i = To mDisplayMemberLength
temp = temp & IIf(i > 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 = )
mDataViewRowFilter = filter
Dim i As Integer icol As Integer
Dim newnode As myTreeNode
RemoveHandler cmPositionChanged AddressOf cm_PositionChanged
MeBeginUpdate()
For i = To mDataViewCount()
newnode = New myTreeNode(GetDisplay(i) GetValue(i) GetPid(i) GetCPtr(i))
当有子结点时为这个结点添加一个空子结点
If newnodeCPtr Then
Dim nullnode As New myTreeNode()
nullnodeValue = NoExpandNodeValue
newnodeNodesAdd(nullnode)
End If
If pnode Is Nothing Then
MeNodesClear()
MeNodesAdd(newnode)
Else
pnodeNodesAdd(newnode)
End If
Next
MeEndUpdate()
mDataViewRowFilter =
AddHandler cmPositionChanged AddressOf cm_PositionChanged
End Sub
在展开有子结点的结点前删除所有子结点再用FillTree为待展开结点新增子结点
Private Sub dbTreeView_BeforeExpand(ByVal sender As Object ByVal e As SystemWindowsFormsTreeViewCancelEventArgs) Handles MyBaseBeforeExpand
当是新增结点引起BeforeExpand事件时直接退出
If ExpandWhenAddNode Then Exit Sub
在展开结点前更新子结点
Dim currentnode As myTreeNode = CType(eNode myTreeNode)
With currentnode
NodesClear()
FillTree(currentnode mPidMember & = & CInt(Value))
End With
End Sub
实现数据与绑定控件的同步
要实现两个方面的同步:
其它绑定控件(如textbox等)应与TreeView当前结点所指向的记录位置一致
Private Sub dbTreeView_AfterSelect(ByVal sender As Object ByVal e As SystemWindowsFormsTreeViewEventArgs) Handles MyBaseAfterSelect
If eNode Is Nothing Then Exit Sub
定位到position
cmPosition = GetPosition(CType(eNode myTreeNode)Value)
If AllowEdit Then
oldNode = eNode
oldPos = cmPosition
End If
End Sub
在其它绑定控件改变了数据源后更新树结点这个工作在触发CurrencyManager的PositionChanged事件时进行
Public Sub cm_PositionChanged(ByVal sender As Object ByVal e As SystemEventArgs)
If CType(MeSelectedNode myTreeNode)Value <> GetValue(cmPosition) Then
DebugWriteLine(Current node isnt correct point to currencymanagerposition!)
MeSelectedNode = FindNodeByValue(GetValue(cmPosition) MeNodes)
End If
If AllowEdit Then
If MeSelectedNode Is Nothing AndAlso cmPosition = cmCount Then
当新增记录时新增树结点
If CType(cmCurrent DataRowView)IsNew Then
MeSelectedNode = AddNode(cmPosition)
Exit Sub
End If
End If
If Not oldNode Is Nothing Then
If CType(oldNode myTreeNode)Value = GetValue(oldPos) Then
更新老结点
oldNodeText = GetDisplay(oldPos)
Else
End If
End If
End If
End Sub
使用dbTreeView
程序运行后界面如下:
相关代码请参见源程序这里不做详述
需要注意的是删除操作并没有删除子结点只是删除当前结点而已删除子结点的工作应该在存储过程中递归实现而不应放在前端