Results 1 to 6 of 6

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

Threaded View

  1. #1

    Thread Starter
    VB-aholic & Lovin' It LaVolpe's Avatar
    Join Date
    Oct 2007
    Location
    Beside Waldo
    Posts
    19,541

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

    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, or reserved white space for icons, when icons (ImageList) are used and no icon is assigned. Looks kinda ugly. But we can avoid this with a little API help.

    Name:  treeIcons.png
Views: 2728
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, consider the icons in the imagelist as zero-bound even though you can reference them directly as one-bound. Because they are considered zero-bound, and zero index is used to clear the icon, the 1st icon in the image list is simply not used.

    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.
    Edited: See post #2 for an alternate method of retrieving the Node ID
    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 tView.SelectedItem Is Node Then ' quick access
            hItem = SendMessage(tView.hWnd, TVM_GETNEXTITEM, TVGN_CARET, ByVal 0&)
        ElseIf Node Is Node.Root Then ' quick access
            hItem = SendMessage(tView.hWnd, TVM_GETNEXTITEM, TVGN_ROOT, ByVal 0&)
        Else
            Set cMoves = New Collection
            
            Set rNode = Node.Root                   ' used for reference
            Do Until tNode.Parent Is Nothing        ' navigate up the tree from passed node
                Set rNode = tNode.Parent.Child      ' used for reference
                Do Until tNode Is rNode
                    cMoves.Add TVGN_NEXT            ' inverse navigate using NEXT
                    Set tNode = tNode.Previous
                Loop
                cMoves.Add TVGN_CHILD               ' inverse navigate using CHILD
                Set tNode = tNode.Parent
            Loop
            Set tNode = Node                        ' used for reference
            If Not tNode Is rNode Then              ' at leaf top level, if not root, continue
                Do Until tNode Is rNode
                    cMoves.Add TVGN_NEXT            ' inverse navigate using NEXT
                    Set tNode = tNode.Previous
                Loop
            End If
            ' now navigate to the desired node from the root
            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. Edited. Can use TVM_GETITEMSTATE instead, passing result of pvGetTreeItem for wParam and ByVal &HFFFF& as the lParam in SendMessage
    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 Files Attached Files
    Last edited by LaVolpe; Oct 13th, 2017 at 08:36 AM. Reason: tweaked pvGetTreeItem and added more comments
    Insomnia is just a byproduct of, "It can't be done"

    Classics Enthusiast? Here's my 1969 Mustang Mach I Fastback. Her sister '67 Coupe has been adopted

    Newbie? Novice? Bored? Spend a few minutes browsing the FAQ section of the forum.
    Read the HitchHiker's Guide to Getting Help on the Forums.
    Here is the list of TAGs you can use to format your posts
    Here are VB6 Help Files online


    {Alpha Image Control} {Memory Leak FAQ} {Unicode Open/Save Dialog} {Resource Image Viewer/Extractor}
    {VB and DPI Tutorial} {Manifest Creator} {UserControl Button Template} {stdPicture Render Usage}

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width