Option Explicit
Private hwndTV As Long
Private Sub Form_Load()
Dim Node1 As Node
Dim Node2 As Node
Dim Node3 As Node
Dim i As Integer
Dim j As Integer
Dim k As Integer
'Set some treeview properties, and
'fill up the treeview with two root
'items, each having a child node with
'with four children...
With TreeView1
'For convenience...
.HideSelection = False
.LabelEdit = tvwManual
hwndTV = .hwnd
'Use the API to set the checkbox style
Call SetTVStyle(hwndTV)
For i = 1 To 2
Set Node1 = .Nodes.Add(, tvwLast, , "Root" & i, 1)
For j = 1 To 1
Set Node2 = .Nodes.Add(Node1.Index, _
tvwChild, , _
"Root" & i & "Child" & j, 2)
For k = 1 To 4
Set Node3 = .Nodes.Add(Node2.Index, _
tvwChild, , _
"Grandchild" & k, 3)
Next 'k
Node2.Expanded = True
Next 'j
Node1.Expanded = True
Next 'i
End With
'Let Label1 reflect the first root Node's text
'and check state. This is subsequently done
'in the NodeClick event, but here on loading,
'a NodeClick is not generated when the treeview
'first appears, even though the first root
'is selected). So we manually call the routine.
Call DisplaySelectedNodeState(TreeView1.Nodes(1), False)
End Sub
Private Function SetTVStyle(hwndTV As Long) As Boolean
Dim dwStyle As Long
dwStyle = GetWindowLong(hwndTV, GWL_STYLE)
'Set the treeview checkbox style. Note that
'this style is applied across the entire
'treeview - you can not have some items
'allowing checks while others don't. If this
'functionality is needed, you must use your
'own state images to mimic the checkboxes.
'This is not covered in this code example.
If dwStyle Then
SetTVStyle = CBool(SetWindowLong(hwndTV, _
GWL_STYLE, _
dwStyle Or TVS_CHECKBOXES))
End If
End Function
Private Sub DisplaySelectedNodeState(sNode As String, _
fIsChecked As Boolean)
If fIsChecked Then
Label1 = sNode & " is checked"
Else: Label1 = sNode & " is unchecked"
End If
End Sub
Private Sub TreeView1_MouseUp(Button As Integer, _
Shift As Integer, _
x As Single, _
y As Single)
'The only way to determine if a Node is checked or
'not is by sending the treeview a TVM_GETITEM
'message and retrieving the Node's checked state.
'In order to send this message, we must have the
'treeview item handle of the Node. The easiest
'way to obtain the Node's item handle is by sending
'the treeview a TVM_HITTEST message with the
'co-ordinates of the mouse when the left button
'is released.
Dim nodeSel As Node
Dim fChecked As Boolean
'Only perform this if the 'use MouseUp'
'option was selected.
If optMouseUp Then
If Button = vbLeftButton Then
'Get the left-clicked node
Set nodeSel = TreeView1.HitTest(x, y)
If (nodeSel Is Nothing) = False Then
fChecked = IsTVItemCheckedFromClick(hwndTV, _
x / Screen.TwipsPerPixelX, _
y / Screen.TwipsPerPixelY)
Call DisplaySelectedNodeState(nodeSel.Text, fChecked)
End If '(nodeSel Is Nothing) = False
End If 'Button = vbLeftButton
End If 'optMouseUp
End Sub
Private Sub TreeView1_NodeClick(ByVal Node As ComctlLib.Node)
'We could do things a bit differently here and
'obtain the treeview item handle of the Node with
'the GetTVItemFromNode call, and pass the handle
'directly to the IsTVItemChecked proc. But since
'we don't determine if this event was invoked due
'to a checkbox click (toggling the Node's checkbox
'state), IsTVItemChecked will not return an accurate
'value.
Dim fChecked As Boolean
Dim hItem As Long
Dim pt As POINTAPI
'Only perform this if the 'use node'
'option was selected.
If optNodeClick Then
'If this event was invoked from a left
'mouse button click (if the left mouse
'button was depressed when the NM_CLICK
'message was received by the treeview
'parent window).
If GetAsyncKeyState(vbKeyLButton) Then
'Get the current cursor pos in screen
'coords, convert it to treeview coords,
'and get the check state.
Call GetCursorPos(pt)
Call ScreenToClient(hwndTV, pt)
fChecked = IsTVItemCheckedFromClick(hwndTV, pt.x, pt.y)
Call DisplaySelectedNodeState(Node.Text, fChecked)
End If 'GetAsyncKeyState
End If 'optNodeClick
End Sub
Private Sub cmdCheckChildren_Click()
'check the children of the selected item
Call SetCheckStateOfChildren(TreeView1.SelectedItem, True)
End Sub
Private Sub cmdUncheckChildren_Click()
'uncheck the children of the selected item
Call SetCheckStateOfChildren(TreeView1.SelectedItem, False)
End Sub
Private Sub SetCheckStateOfChildren(nodeParent As Node, _
fCheck As Boolean)
Dim nodeChild As Node
Dim hItem As Long
Set nodeChild = nodeParent.Child
Do While (nodeChild Is Nothing) = False
'obtain the item handle of the node
hItem = GetTVItemFromNode(hwndTV, nodeChild)
'if a valid handle, set the checked state
If hItem Then Call SetTVItemCheckImage(hwndTV, hItem, fCheck)
'if the node has child nodes itself,
'recursively call this routine to set
'the state of those as well
If (nodeChild.Child Is Nothing) = False Then
Call SetCheckStateOfChildren(nodeChild, fCheck)
End If
Set nodeChild = nodeChild.Next
Loop
End Sub
' Form2
' --------------------------------------
Private Sub Command1_Click()
'lets us toggle fCheck between True and False
Static fCheck As Boolean
'required for demo
Dim tvi As TVITEM
'--------------------------------------
'toggle the flag
fCheck = Not fCheck
'this sets the tvi.state value via the conventional
'If...Then method
If fCheck Then
tvi.state = INDEXTOSTATEIMAGEMASK(IIL_CHECKED)
Else: tvi.state = INDEXTOSTATEIMAGEMASK(IIL_UNCHECKED)
End If
'Click the button twice.
'When fCheck is true, the output from the above will be:
'
'True 8192
'False 4098
Print fCheck, tvi.state,
'--------------------------------------
'now, use the ABS method to set the parameter
tvi.state = INDEXTOSTATEIMAGEMASK(Abs(fCheck) + 1)
'..and again, print out the return values.
'Now the output line will be:
'
'True 8192 2 8192
'False 4098 1 4096
Print Abs(fCheck) + 1, tvi.state
'--------------------------------------
'Therefore, the following line:
'tvi.state = INDEXTOSTATEIMAGEMASK(Abs(fCheck) + 1)
'is equivalent to:
'If fCheck Then
' tvi.state = INDEXTOSTATEIMAGEMASK(IIL_CHECKED)
'Else: tvi.state = INDEXTOSTATEIMAGEMASK(IIL_UNCHECKED)
'End If
'when the values being flipped are 1 and 2
End Sub