Solved

TreeView & Checkboxes

Posted on 1998-11-19
6
568 Views
Last Modified: 2013-12-25
You can put graphics next to the text in a Node in a TreeView control

Can I do the equiv of checkboxes to the left of the text. I want to do this because I'm doing an Explorer style UI which would let users click on the checkbox to select a file for inclusion

Failing that, how can I change the graphic of a node. I don't mean ExpandedImage
0
Comment
Question by:inetcomm
  • 2
  • 2
  • 2
6 Comments
 
LVL 8

Expert Comment

by:MikeP090797
Comment Utility
You can modify the .Image property of every node at any time, so when the user clicks on an item, you need to find the mouse coordinates,and change the image accordingly
0
 
LVL 13

Expert Comment

by:Mirkwood
Comment Utility
I don't think MikeP answer is what you were asking. So here is a better one (I think).

Here is a reference to the site that has a great sample.
http://www.mvps.org/vbnet/code/comctl/tvcheckbox.htm

Here are the declarations
Option Explicit
'
'Brad Martinez, http://www.mvps.org/ccrp/
'

'These are the indices of the treeview
'checkbox state images when the treeview
'TVS_CHECKBOXES style bit is set.
Public Const IIL_UNCHECKED As Long = 1
Public Const IIL_CHECKED As Long = 2

Public Const GWL_STYLE = (-16)

Declare Function GetWindowLong Lib "user32" _
     Alias "GetWindowLongA" _
    (ByVal hwnd As Long, _
     ByVal nIndex As Long) As Long
     
Declare Function SetWindowLong Lib "user32" _
     Alias "SetWindowLongA" _
    (ByVal hwnd As Long, _
     ByVal nIndex As Long, _
     ByVal dwNewLong As Long) As Long

Declare Function SendMessageAny Lib "user32" _
    Alias "SendMessageA" _
    (ByVal hwnd As Long, _
     ByVal wMsg As Long, _
     ByVal wParam As Any, _
     lParam As Any) As Long  

Public Type POINTAPI   'pt
  x As Long
  y As Long
End Type

Declare Function GetCursorPos Lib "user32" _
     (lpPoint As POINTAPI) As Long
     
Declare Function ScreenToClient Lib "user32" _
    (ByVal hwnd As Long, _
     lpPoint As POINTAPI) As Long
     
Declare Function GetAsyncKeyState Lib "user32" _
    (ByVal vKey As Long) As Integer

'--------------------------------------------
'treeview definitions defined in Commctrl.h at:
'http://premium.microsoft.com/msdn/library/sdkdoc/c67_4c8m.htm

'style
Public Const TVS_CHECKBOXES  As Long = &H100  '>= IE3

'messages
Public Const TV_FIRST      As Long = &H1100
Public Const TVM_GETITEM   As Long = (TV_FIRST + 12)
Public Const TVM_SETITEM   As Long = (TV_FIRST + 13)
Public Const TVM_HITTEST   As Long = (TV_FIRST + 17)

Public Type TVITEM   'was TV_ITEM
  mask As Long
  hItem As Long
  state As Long
  stateMask As Long
  pszText As String   'Long   'pointer
  cchTextMax As Long
  iImage As Long
  iSelectedImage As Long
  cChildren As Long
  lParam As Long
End Type

'TVITEM.mask flags
Public Const TVIF_TEXT     As Long = &H1
Public Const TVIF_STATE    As Long = &H8
Public Const TVIF_HANDLE   As Long = &H10

'TVITEM.state bit value
Public Const TVIS_STATEIMAGEMASK  As Long = &HF000

Public Type TVHITTESTINFO   'was TV_HITTESTINFO
  pt As POINTAPI
  flags As Long
  hItem As Long
End Type

'TVHITTESTINFO.flags value
Public Const TVHT_ONITEMSTATEICON  As Long = &H40

'User-defined as the maximum treeview item
'text length. If an items text exceeds this
'value when calling GetTVItemText there could
'be problems...
Public Const MAX_ITEM = 256
Public Const TVM_GETNEXTITEM  As Long = (TV_FIRST + 10)

'TVM_GETNEXTITEM wParam values
Public Enum TVGN_Flags
    TVGN_ROOT = &H0
    TVGN_NEXT = &H1
    TVGN_PREVIOUS = &H2
    TVGN_PARENT = &H3
    TVGN_CHILD = &H4
    TVGN_FIRSTVISIBLE = &H5
    TVGN_NEXTVISIBLE = &H6
    TVGN_PREVIOUSVISIBLE = &H7
    TVGN_DROPHILITE = &H8
    TVGN_CARET = &H9
#If (WIN32_IE >= &H400) Then
    TVGN_LASTVISIBLE = &HA
#End If
End Enum


Public Function IsTVItemChecked(hwndTV As Long, _
                                hItem As Long) As Boolean
                               
  '---------------------------------------------------
  'Determines if the current state image of the
  'specified treeview item is set to the checked
  'checkbox image index.
  '
  'hwndTV   - treeview window handle
  'hItem    - item's handle whose checkbox state is to be to returned
  '
  'Returns True if the item's state image is
  'set to the checked checkbox index, returns
  'False otherwise.
  '---------------------------------------------------
                               
   Dim tvi As TVITEM

  'Initialize the struct and get the item's state value.
   With tvi
      .mask = TVIF_STATE
      .hItem = hItem
      .stateMask = TVIS_STATEIMAGEMASK
   End With
   
   Call TreeView_GetItem(hwndTV, tvi)

  'We have to test to see if the treeview
  'checked state image *is* set since the logical
  'And test on the unchecked image (1) will
  'evaluate to True when either checkbox image
  'is set.
   IsTVItemChecked = (tvi.state And INDEXTOSTATEIMAGEMASK(IIL_CHECKED))
 
End Function


Public Function IsTVItemCheckedFromClick(hwndTV As Long, _
                                         x As Long, _
                                         y As Long) As Boolean
                                         
  '---------------------------------------------------
  'Determines if the current state image of the
  'item under the specified point (if any) is
  'set to the checked checkbox image index.
  '
  'hwndTV - treeview window handle
  'x, y   - treeview co-ordinates in which
  '         to retrieve the item from
  '
  'Returns True if the item's state image is
  'set to the checked checkbox index, or False
  'otherwise.
  '---------------------------------------------------

   Dim tvhti As TVHITTESTINFO
   Dim fChecked As Boolean
   
   tvhti.pt.x = x
   tvhti.pt.y = y
 
   If TreeView_HitTest(hwndTV, tvhti) Then   'returns an hItem also
   
      fChecked = IsTVItemChecked(hwndTV, tvhti.hItem)
   
     'Since we retrieved the item's handle from
     'a treeview co-ordinate as a result of a
     'NodeClick event (or MouseUp event, both are
     'invoked from a NM_CLICK notification), if
     'this co-ordinate is within the area of the
     'item's state icon, then the item's checkbox
     'image is *in the process* of being toggled,
     'but *not yet* toggled. So we'll toggle the
     'return value reflecting the soon-to-be-set
     'state value.
      If (tvhti.flags And TVHT_ONITEMSTATEICON) Then fChecked = Not fChecked
   
      IsTVItemCheckedFromClick = fChecked
 
   End If
 
End Function


Public Function SetTVItemCheckImage(hwndTV As Long, _
                                    hItem As Long, _
                                    fCheck As Boolean) As Boolean
                                   
  '---------------------------------------------------
  'Set the specified checkbox state for the
  'specified item. Returns True if successful,
  'returns False otherwise.
  '
  'hwndTV   - treeview window handle
  'hItem    - item's handle whose checkbox state is to be to set
  'fCheck   - If True, sets the checkbox state to the checked image,
  '           if False, sets the unchecked image.
  '---------------------------------------------------

   Dim tvi As TVITEM
 
   With tvi
      .mask = TVIF_HANDLE Or TVIF_STATE
      .hItem = hItem
      .stateMask = TVIS_STATEIMAGEMASK
 
     'As the values for the check constants are 1 for
     'unchecked (IIL_UNCHECKED) and 2 for checked
     '(IIL_CHECKED), fCheck (which is either True or
     'False) can be used directly to toggle the
     'INDEXTOIMAGESTATE parameter.
     '
     'This is accomplished by using the ABS() of
     'fCheck (turning True (-1) and False (0)
     'into 1 and 0 respectively.) Now, by adding 1,
     'the value toggles between 2 and 1 respectively,
     'exactly the same as using the IIL_ constants.
     '
     'Therefore, the single line of code below is
     'equivalent to an If..Then statement of:
     '
     'If fCheck Then
     '      tvi.state = INDEXTOSTATEIMAGEMASK(IIL_CHECKED)
     'Else: tvi.state = INDEXTOSTATEIMAGEMASK(IIL_UNCHECKED)
     'End If
     '
     'See the comments section for code that more
     'clearly demonstrates using ABS() to achieve this.
     
      .state = INDEXTOSTATEIMAGEMASK(Abs(fCheck) + 1)
   
   End With
     
   SetTVItemCheckImage = TreeView_SetItem(hwndTV, tvi)
 
End Function


Public Function GetTVItemText(hwndTV As Long, _
                              hItem As Long, _
                              Optional cbItem As Long = MAX_ITEM) As String
 
  '---------------------------------------------
  'Returns the text of the specified treeview
  'item if successful, returns an empty string
  'otherwise.
  '
  'hwndTV   - treeview window handle
  'hItem    - item's handle whose text is to be to returned
  'cbItem   - length of the specified item's text.
  '---------------------------------------------
   
   Dim tvi As TVITEM
   
   With tvi
      .mask = TVIF_TEXT
      .hItem = hItem
      .pszText = String$(cbItem, 0)
      .cchTextMax = cbItem
   End With
   
   If TreeView_GetItem(hwndTV, tvi) Then
      GetTVItemText = GetStrFromBufferA(tvi.pszText)
   End If
 
End Function


Public Function GetStrFromBufferA(item As String) As String

  'Returns the string before first null char
  'encountered (if any) from an ANSII string.

  If InStr(item, vbNullChar) Then
    GetStrFromBufferA = Left$(item, InStr(item, vbNullChar) - 1)
 
  Else
   
     'If item had no null char, the Left$ function
     'above would return a zero length string ("").
      GetStrFromBufferA = item
 
  End If

End Function


Public Function GetTVItemFromNode(hwndTV As Long, _
                                  nod As Node) As Long
   
   
  'If successful, returns the treeview item
  'handle represented by the specified Node,
  'returns 0 otherwise.
   
   Dim nodeCur As Node
   Dim asNodes() As String
   Dim nNodes As Integer
   Dim i As Integer
   Dim hitemParent As Long
   Dim hItem As Long
   
   Set nodeCur = nod
   
  'Cache the node and all of it's parent
  'node's text in the array
   Do While (nodeCur Is Nothing) = False
      nNodes = nNodes + 1
     
      ReDim Preserve asNodes(nNodes)
      asNodes(nNodes) = nodeCur.Text
     
      Set nodeCur = nodeCur.Parent
   Loop

  'Get the hItem of the first root in the
  'treeview, it will be the first parent
   hitemParent = TreeView_GetRoot(hwndTV)
   
   If hitemParent Then
   
     'Walk through the cached node text from
     'the root to the specified node (backwards
     'through the array)
     
      Do While nNodes
     
        'Get the hItem of the current node
         hItem = FindTVItemFromText(hwndTV, _
                                    hitemParent, _
                                    asNodes(nNodes))
         
         If hItem Then
         
           'Make the the current parent's first
           'child item the new parent
            hitemParent = TreeView_GetChild(hwndTV, hItem)
           
         Else: Exit Function
         End If
     
         nNodes = nNodes - 1
   
      Loop
 
      GetTVItemFromNode = hItem
   
   End If
 
End Function


Public Function FindTVItemFromText(hwndTV As Long, _
                                   ByVal hitemChild As Long, _
                                   sItem As String) As Long
                                   
  '---------------------------------------------
  'Returns the first encountered item handle
  'whose text label matches the specified text.
  '*Is case sensitive*.
  '
  'hwndTV      - treeview window handle
  'hitemChild  - first sibling item's handle in which to search
  'sItem       - specified item's text we're looking for
  '
  'If the text represented by sItem is found, it's
  'hItem is returned, otherwise 0 is returned.
  '---------------------------------------------
                                 
  'Can't find the hItem of an item with no text...
   If Len(sItem) = 0 Then Exit Function
   
   Do While hitemChild
   
    'If the current sibling item label
    'matches our target text, we're done.
   
      If GetTVItemText(hwndTV, hitemChild, MAX_ITEM) = sItem Then
         FindTVItemFromText = hitemChild
         Exit Function
      End If
   
     'Keep going while we have subsequent
     'sibling items
      hitemChild = TreeView_GetNextSibling(hwndTV, hitemChild)
 
  Loop
 
End Function


Public Function TreeView_HitTest(hwnd As Long, _
                                 lpht As TVHITTESTINFO) As Long
 
  'Determines the location of the specified point
  'relative to the client area of a treeview control.
  'Returns the handle to the tree-view item that
  'occupies the specified point or NULL if no item
  'occupies the point.

   TreeView_HitTest = SendMessageAny(hwnd, TVM_HITTEST, 0&, lpht)
   
End Function


Public Function TreeView_GetItem(hwnd As Long, pitem As TVITEM) As Boolean
   
  'Retrieves some or all of a tree-view
  'item's attributes. Returns TRUE if
  'successful or FALSE otherwise.

   TreeView_GetItem = SendMessageAny(hwnd, TVM_GETITEM, 0&, pitem)
   
End Function


Public Function TreeView_SetItem(hwnd As Long, pitem As TVITEM) As Boolean

  'Sets some or all of a tree-view item's
  'attributes. Old docs say returns zero if
  'successful or - 1 otherwise.
  'New docs say returns TRUE if successful,
  'or FALSE otherwise!
   
   TreeView_SetItem = SendMessageAny(hwnd, TVM_SETITEM, 0&, pitem)
 
End Function


Public Function INDEXTOSTATEIMAGEMASK(iState As Long) As Long

  'Prepares the index of a state image so that a
  'treeview control or listview control can use the
  'index to retrieve the state image for an item.
  'Returns the one-based index of the state image
  'shifted left twelve bits. A common control
  'utility macro.
  'This macro is defined in commctrl.h as:
  '#define INDEXTOSTATEIMAGEMASK(i) ((i) << 12)
 
   INDEXTOSTATEIMAGEMASK = iState * (2 ^ 12)

End Function


Public Function TreeView_GetNextItem(hwnd As Long, _
                                     hItem As Long, _
                                     flag As Long) As Long

  'Retrieves the tree-view item that bears the
  'specified relationship to a specified item.
  'Returns the handle to the item if successful
  'or 0 otherwise.

   TreeView_GetNextItem = SendMessageAny(hwnd, _
                                      TVM_GETNEXTITEM, _
                                      flag, _
                                      ByVal hItem)
   
End Function


Public Function TreeView_GetChild(hwnd As Long, hItem As Long) As Long
 
  'Retrieves the first child item. The hitem
  'parameter must be NULL. Returns the handle
  'to the item if successful or 0 otherwise.
     
   TreeView_GetChild = TreeView_GetNextItem(hwnd, hItem, TVGN_CHILD)
 
End Function


Public Function TreeView_GetNextSibling(hwnd As Long, _
                                        hItem As Long) As Long

   'Retrieves the next sibling item.
   'Returns the handle to the item if
   'successful or 0 otherwise.

   TreeView_GetNextSibling = TreeView_GetNextItem(hwnd, hItem, TVGN_NEXT)
   
End Function


Public Function TreeView_GetRoot(hwnd As Long) As Long

  'Retrieves the topmost or very first item
  'of the tree-view control. Returns the handle
  'to the item if successful or 0 otherwise.
   
   TreeView_GetRoot = TreeView_GetNextItem(hwnd, 0, TVGN_ROOT)
 
End Function
0
 
LVL 8

Expert Comment

by:MikeP090797
Comment Utility
Reject my answer and accept Mirkwood's, as his answer is obviosly better
0
IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

 

Author Comment

by:inetcomm
Comment Utility
thank-you Mirkwood.

I was referred to Brad's code by K2@m-net through Microsoft's open News Server & have tried it. It DOES do almost everything I need.

Before I close this off & award you the points, do you, or anyone, have code which modifies the State Image list to include a Greyed-out checkbox. I need to indicate Selected files (Checked), Deselected files (Unchecked) & Directories containing both (Greyed-out Checkbox)
0
 
LVL 13

Accepted Solution

by:
Mirkwood earned 120 total points
Comment Utility
Setting up state images



------------------------------------------------------------------------


As you are already aware, the tree view control can display two images for each item. Each of these two images belong in different image list controls that have to be associated with the tree control. Whereas, an item can have upto 256 item images, it can only have 15 state images. Nevertheless, the programming for the state images is very similar to the programming for item images.

An image list has to be created before it can be associated with the tree view control. You can create the image list from a single bitmap, horizontally laid out to contain all the images or you can add individual icons to the image list.

Step 1: Create the bitmap

Add a bitmap resource in the resource editor that contains all the icons. Here is an example below. The individual icons in this bitmap are 13x13 pixels, but you may choose a different size and it need not be a square. The first image in the list is not used.

 <Picture>

Step 2: Add a member variable to hold the image list

The member variable is usually added to the class that is responsible for setting up the tree view control. This would usually be the CView derivative or a CDialog derivative. You can also add the member variable to the sub-class of CTreeCtrl which is what I recommend.
 

class CTreeCtrlX : public CTreeCtrl
{
// Construction
public:
        CTreeCtrlX();

// Attributes
public:
    CImageList m_imageState ;
    :
    :
    :

}


Step 3: Create and set the image list

Call the Create() function of the image object with the resource id of the bitmap we created in step 1 and the width of each icon. The height of the icons is automatically set to the height of the bitmap. The third argument required by the Create() function represents the number of new images the resized image list can contain. Since we are creating the image list from a bitmap, we would normally not add any more images at runtime so we let this value be one. The last argument is the mask color. That is, all the pixels of this color will behave as a transparent color. Since normally the window color is white, we set the mask color to white.

Once the image list has been created, the tree view control has to be instructed to use it. We do that by calling the SetImageList() function. The following statement usually belongs in the OnInitDialog() function or the OnInitialUpdate() function. Note that in the call to SetImageList(), the second argument is TVSIL_STATE.
 

        m_tree.m_imageState.Create( IDB_STATE, 13, 1, RGB(255,255,255) );
        m_tree.SetImageList( &(m_tree.m_imageState), TVSIL_STATE );




Step 4: Specify state icons for the items

Once an image list has been associated with the tree view control, you can instruct the tree view control to the use the state image you want when inserting an item to the control. You can also use the SetItemState() image function. In both the cases you have to use the macro INDEXTOSTATEIMAGEMASK(). This macro essentially rearranges the bits of the index value to be compatible with what the tree view control expects. To remove the state image for an item, use the index zero. Here are example usages –
 

        // Using TV_INSERTSTRUCT
        CString str = "xyzASDFqwerZCV";
        TV_INSERTSTRUCT tv_is;
        tv_is.hParent = parent ? parent : TVI_ROOT;
        tv_is.hInsertAfter = TVI_LAST ;
        tv_is.item.mask = TVIF_TEXT | TVIF_STATE;
        tv_is.item.stateMask = TVIS_STATEIMAGEMASK;
        tv_is.item.state = INDEXTOSTATEIMAGEMASK( 1 );
        tv_is.item.pszText = str.GetBuffer(1);
        tv_is.item.cchTextMax = str.GetLength();
        hItem = InsertItem( &tv_is );
        str.ReleaseBuffer();


        // Using SetItemState
        SetItemState( hItem, INDEXTOSTATEIMAGEMASK(1), TVIS_STATEIMAGEMASK );


 
0
 

Author Comment

by:inetcomm
Comment Utility
Mirkwood,

thank-you very much
0

Featured Post

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

Introduction I needed to skip over some file processing within a For...Next loop in some old production code and wished that VB (classic) had a statement that would drop down to the end of the current iteration, bypassing the statements that were c…
If you need to start windows update installation remotely or as a scheduled task you will find this very helpful.
Get people started with the process of using Access VBA to control Excel using automation, Microsoft Access can control other applications. An example is the ability to programmatically talk to Excel. Using automation, an Access application can laun…
This lesson covers basic error handling code in Microsoft Excel using VBA. This is the first lesson in a 3-part series that uses code to loop through an Excel spreadsheet in VBA and then fix errors, taking advantage of error handling code. This l…

763 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

13 Experts available now in Live!

Get 1:1 Help Now