Quantcast
Channel: VBForums - CodeBank - Visual Basic 6 and earlier
Viewing all articles
Browse latest Browse all 1449

[vb6]Treeview - Prevent from indenting when no icon is used

$
0
0
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
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

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.
Code:

' change Treeview1 & ImageList1 as needed
    SendMessage TreeView1.hWnd, TVM_SETIMAGELIST, TVSIL_STATE, ByVal ImageList1.hImageList

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.
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

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
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

2. Retrieving which icon is assigned
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

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.
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

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.
Attached Images
 
Attached Files

Viewing all articles
Browse latest Browse all 1449

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>