Just a neat option. It has its limitations. It works for both versions 5 & 6 of the common controls TreeView.
In the screenshot below, you'll notice that the left image has indentation when icons (ImageList) is used and no icon is assigned. Looks kinda ugly. But we can avoid this with a little API help.
![Name: treeIcons.png
Views: 46
Size: 7.2 KB]()
Limitations:
1. You cannot use any treeview style that includes icons, i.e., not tvwTreeLinesPictureText
2. You cannot use the checkbox style (image above uses icons vs checkbox style)
3. The number of different icons you can use is limited to 15 maximum
4. You must add a bogus icon in the ImageList. This bogus icon is always the 1st one
The reason for 15 max icons is that this API option only allows 4 bits to identify an icon index. With only 4 bits, we have a maximum range of 0 to 15. The value 0 is used to clear the icon, values 1 thru 15 are the possible icons in your image list, starting with the 2nd icon in the list. So icon #2 in the list, is #1 when using the APIs. Because first icon is really the second in the image list, an unused icon is needed as the first icon in the image list
Here are the APIs used
The image list must be assigned via this call. Should be added in Form_Load.
Note that you still associate the ImageList via the Treeview property page, as normal.
This helper function is used by the other 3 public functions. Purpose is to retrieve the node ID (assigned by the window) not the index/key assigned by the control. It does this by reverse navigating from the target node to the 1st (root) node in the tree.
Here are three functions that do what we'll want. Can be placed in a module or your form
1. Setting the icon from the image list
2. Retrieving which icon is assigned
3. This is optional. A method to determine if user is clicking on the icon. See the sample project to see how the Node_Click event uses this method.
In the sample project, you can click on the icons to toggle the "checkmark"
Oops. Left in some test code. Re-uploaded the zip to fix that.
In the screenshot below, you'll notice that the left image has indentation when icons (ImageList) is used and no icon is assigned. Looks kinda ugly. But we can avoid this with a little API help.
Limitations:
1. You cannot use any treeview style that includes icons, i.e., not tvwTreeLinesPictureText
2. You cannot use the checkbox style (image above uses icons vs checkbox style)
3. The number of different icons you can use is limited to 15 maximum
4. You must add a bogus icon in the ImageList. This bogus icon is always the 1st one
The reason for 15 max icons is that this API option only allows 4 bits to identify an icon index. With only 4 bits, we have a maximum range of 0 to 15. The value 0 is used to clear the icon, values 1 thru 15 are the possible icons in your image list, starting with the 2nd icon in the list. So icon #2 in the list, is #1 when using the APIs. Because first icon is really the second in the image list, an unused icon is needed as the first icon in the image list
Here are the APIs used
Code:
Private Declare Function SendMessage Lib "user32.dll" Alias "SendMessageA" (ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByRef lParam As Any) As Long
Private Const TV_FIRST As Long = &H1100
Private Const TVM_GETITEMA As Long = (TV_FIRST + 12)
Private Const TVM_SETITEMA As Long = (TV_FIRST + 13)
Private Const TVM_GETNEXTITEM As Long = (TV_FIRST + 10)
Private Const TVM_GETITEMRECT As Long = (TV_FIRST + 4)
Private Const TVM_SETIMAGELIST As Long = (TV_FIRST + 9)
Private Const TVSIL_STATE As Long = 2
Private Const TVIF_STATE As Long = &H8
Private Const TVGN_ROOT As Long = &H0
Private Const TVGN_CHILD As Long = &H4
Private Const TVGN_NEXT As Long = &H1
Private Const TVGN_CARET As Long = &H9
Note that you still associate the ImageList via the Treeview property page, as normal.
Code:
' change Treeview1 & ImageList1 as needed
SendMessage TreeView1.hWnd, TVM_SETIMAGELIST, TVSIL_STATE, ByVal ImageList1.hImageList
Code:
Private Function pvGetTreeItem(tView As TreeView, Node As Node) As Long
Dim rNode As Node, tNode As Node
Dim cMoves As Collection
Dim c As Long, hItem As Long
If Node Is Node.Root Then
hItem = SendMessage(tView.hWnd, TVM_GETNEXTITEM, TVGN_ROOT, ByVal 0&)
ElseIf tView.SelectedItem Is Node Then
hItem = SendMessage(tView.hWnd, TVM_GETNEXTITEM, TVGN_CARET, ByVal 0&)
Else
Set cMoves = New Collection
Set rNode = Node.Root
Set tNode = Node
Do Until tNode.Parent Is Nothing
Do Until tNode.Previous Is Nothing
cMoves.Add TVGN_NEXT ' "next sibling"
Set tNode = tNode.Previous
Loop
cMoves.Add TVGN_CHILD ' "first child"
Set tNode = tNode.Parent
Loop
If Not tNode Is rNode Then
cMoves.Add TVGN_NEXT ' "next sibling"
Do Until tNode.Previous Is rNode
cMoves.Add TVGN_NEXT ' "next sibling"
Set tNode = tNode.Previous
Loop
End If
hItem = SendMessage(tView.hWnd, TVM_GETNEXTITEM, TVGN_ROOT, ByVal 0&)
For c = cMoves.Count To 1 Step -1
hItem = SendMessage(tView.hWnd, TVM_GETNEXTITEM, cMoves(c), ByVal hItem)
If hItem = 0 Then Exit For
Next
Set cMoves = Nothing
End If
pvGetTreeItem = hItem
End Function
1. Setting the icon from the image list
Code:
Public Sub SetNodeIcon(tView As TreeView, Node As Node, ZeroBoundIconIndex As Long)
If Node Is Nothing Or tView Is Nothing Then Exit Sub
If ZeroBoundIconIndex < 0 Then Exit Sub
If ZeroBoundIconIndex > 15 Then Exit Sub
Dim lAttr(0 To 10) As Long
lAttr(1) = pvGetTreeItem(tView, Node)
If lAttr(1) Then
lAttr(0) = TVIF_STATE
lAttr(3) = &HFFFF&
SendMessage tView.hWnd, TVM_GETITEMA, 0&, lAttr(0)
lAttr(2) = (lAttr(2) And &HFFFF0FFF) Or (&H1000& * ZeroBoundIconIndex)
SendMessage tView.hWnd, TVM_SETITEMA, 0&, lAttr(0)
End If
End Sub
Code:
Public Function GetNodeIcon(tView As TreeView, Node As Node) As Long
If Node Is Nothing Or tView Is Nothing Then Exit Function
Dim lAttr(0 To 10) As Long
lAttr(1) = pvGetTreeItem(tView, Node)
If lAttr(1) Then
lAttr(0) = TVIF_STATE
lAttr(3) = &HFFFF&
SendMessage tView.hWnd, TVM_GETITEMA, 0&, lAttr(0)
GetNodeIcon = (lAttr(2) And &HF000&) \ &H1000&
End If
End Function
Code:
Public Function MouseOverNodeIcon(tView As TreeView, Node As Node, x As Single) As Boolean
' X must be passed in pixels
If Node Is Nothing Or tView Is Nothing Then Exit Function
Dim tRect(0 To 3) As Long
tRect(0) = pvGetTreeItem(tView, Node)
If tRect(0) Then
If SendMessage(tView.hWnd, TVM_GETITEMRECT, 1&, tRect(0)) Then
MouseOverNodeIcon = (x < tRect(0))
End If
End If
End Function
Oops. Left in some test code. Re-uploaded the zip to fix that.