经过一些研究和几个小时的编码,我自己编写了一个解决方案。
我必须添加一个 ImageList,将其与 TreeView 相关联,并为三个状态中的每一个添加一个复选框的图像。谷歌图片搜索为我节省了一些时间:)。
'Enumeration for simulated tri-state checkboxes, matching up to the TreeView's associated Image List's Index
Private Enum CheckboxEnum
Unchecked = 1
Checked = 2
Partial = 3
End Enum
'---------------------------------------------------------------------------------------
' Procedure : objTreeView_MouseDown
' Author : Matty Brown
' Date : 19/05/2014
' Purpose : Because TreeView doesn't support tri-state checkboxes, these have to be simulated using images.
'---------------------------------------------------------------------------------------
'
Private Sub objTreeView_MouseDown(ByVal Button As Integer, ByVal Shift As Integer, ByVal x As stdole.OLE_XPOS_PIXELS, ByVal y As stdole.OLE_YPOS_PIXELS)
Const CHECKBOX_WIDTH As Integer = 195 '195=13px
Dim objNode As Node
Set objNode = objTreeView.HitTest(x, y)
If objNode Is Nothing Then
'Miss
Else
'Find left side of node by moving left one pixel at a time until you fall off the node, then move one pixel to the right
Dim intX As stdole.OLE_XPOS_PIXELS
For intX = x To 0 Step -15
If Not objNode Is objTreeView.HitTest(intX, y) Then
If x <= intX + CHECKBOX_WIDTH Then
'User clicked on the checkbox
Select Case objNode.Image
Case CheckboxEnum.Unchecked:
objNode.Image = CheckboxEnum.Checked
Case Else:
objNode.Image = CheckboxEnum.Unchecked
End Select
'Recursively check child nodes
Call CheckTreeNodes(objTreeView, objNode, objNode.Image)
'Update parent node(s)
Call UpdateParentNodes(objTreeView, objNode)
Else
'User clicked outside of the checkbox
'
End If
Exit For
End If
Next
End If
End Sub
'---------------------------------------------------------------------------------------
' Procedure : CheckTreeNodes
' Author : Matty Brown
' Date : 16/05/2014
' Purpose : Checks or unchecks all of the child nodes for the specified node
'---------------------------------------------------------------------------------------
'
Private Sub CheckTreeNodes(ByRef tv As TreeView, ByRef nodNode As Node, ByVal Value As CheckboxEnum)
Dim lngIndex As Long
'Cascade change to children
If nodNode.Children > 0 Then
lngIndex = nodNode.Child.Index
Call CheckTreeNodes(tv, tv.Nodes(lngIndex), Value)
Do While lngIndex <> nodNode.Child.LastSibling.Index
lngIndex = tv.Nodes(lngIndex).Next.Index
Call CheckTreeNodes(tv, tv.Nodes(lngIndex), Value)
Loop
End If
nodNode.Image = Value
End Sub
'---------------------------------------------------------------------------------------
' Procedure : CountChildNodes
' Author : Matty Brown
' Date : 19/05/2014
' Purpose : Counts how many child nodes are checked or unchecked, so that a parent node can be set correctly
'---------------------------------------------------------------------------------------
'
Private Sub CountChildNodes(ByRef tv As TreeView, ByRef nodNode As Node, ByRef lngChecked As Long, ByRef lngUnchecked As Long)
Dim lngIndex As Long
'Check this node's children
If nodNode.Children > 0 Then
lngIndex = nodNode.Child.Index
Call CountChildNodes(tv, tv.Nodes(lngIndex), lngChecked, lngUnchecked)
Do While lngIndex <> nodNode.Child.LastSibling.Index
lngIndex = tv.Nodes(lngIndex).Next.Index
Call CountChildNodes(tv, tv.Nodes(lngIndex), lngChecked, lngUnchecked)
Loop
Else
'Update totals
Select Case nodNode.Image
Case CheckboxEnum.Checked:
lngChecked = lngChecked + 1
Case CheckboxEnum.Unchecked:
lngUnchecked = lngUnchecked + 1
End Select
End If
End Sub
'---------------------------------------------------------------------------------------
' Procedure : UpdateParentNodes
' Author : Matty Brown
' Date : 19/05/2014
' Purpose : Steps through parent nodes, updating them according to how many checked/unchecked child nodes they have
'---------------------------------------------------------------------------------------
'
Private Sub UpdateParentNodes(ByRef tv As TreeView, ByRef nodNode As Node)
Dim lngIndex As Long
Dim nodParent As Node
Dim lngChecked As Long, lngUnchecked As Long
'If this node has no parents, there's nothing to update
If nodNode.Parent Is Nothing Then Exit Sub
Set nodParent = nodNode
Do While Not nodParent.Parent Is Nothing
Set nodParent = nodParent.Parent
'Reset counters
lngUnchecked = 0
lngChecked = 0
'Count children
Call CountChildNodes(tv, nodParent, lngChecked, lngUnchecked)
'Update parent nodes
If lngUnchecked = 0 And lngChecked > 0 Then
nodParent.Image = CheckboxEnum.Checked
ElseIf lngUnchecked > 0 And lngChecked > 0 Then
nodParent.Image = CheckboxEnum.Partial
Else
nodParent.Image = CheckboxEnum.Unchecked
End If
Loop
End Sub