1

I have created TreeView like this:

TreeView=CreateWindowEx(0, WC_TREEVIEW, TEXT("Tree View"), WS_VISIBLE | WS_CHILD, 0, 0, 200, 500, hwnd, (HMENU)ID_TREE_VIEW, GetModuleHandle(NULL), NULL);

Now I added one item to it like shown on this website.

It all okay, but after hours and hours of googling I still didn't found answer to these questions:

  1. How to add subitems (nodes)?

  2. How to add checkbox on each item (how to determine if specified checkbox is checked)?

AlwaysLearningNewStuff
  • 2,939
  • 3
  • 31
  • 84
user3213103
  • 133
  • 3
  • 14
  • 2
    Try this treasure chest http://www.codeguru.com/cpp/controls/treeview/ – cup Mar 16 '14 at 19:48
  • I have edited again my answer. This time both clicking the tree's checkbox and pressing spacebar are successfully covered. The only puzzle left to solve is thoroughly described in the bottom of the edit and I believe is a minor problem. Good luck and best regards. – AlwaysLearningNewStuff Mar 17 '14 at 01:48
  • To provide consistent behavior **after you remove a checkbox** you must subclass tree view in order to avoid bug when user presses *spacebar*. You can find detailed explanation and the code [here](http://stackoverflow.com/questions/22708060/removed-checkbox-reappears-in-treeview-node-when-spacebar-is-pressed). This way is required **if you must support XP+** else you might be better off with [`TVN_ITEMCHANGED`](http://msdn.microsoft.com/en-us/library/windows/desktop/bb773526%28v=vs.85%29.aspx) message. – AlwaysLearningNewStuff Mar 31 '14 at 22:19

1 Answers1

8

EDIT #4:

In response to OPs request, I have added an example that removes checkbox from a parent node.

THE PROBLEM IS THAT CHECKBOX STILL APPEARS WHEN USER SELECTS A NODE AND PRESSES SPACEBAR.

This question solves that problem.

EDIT #3:

I have added the code that creates a node that is already checked.

It is the second child bode in the WM_CREATE handler.

END OF EDIT

case WM_CREATE:
    {
        // this is your treeview

        TreeView = CreateWindowEx(0, WC_TREEVIEW, 
            TEXT("Tree View"), WS_VISIBLE | WS_CHILD, 
            0, 0, 200, 500, 
            hwnd, (HMENU)ID_TREE_VIEW, GetModuleHandle(NULL), NULL);

        /************ enable checkboxes **************/

        DWORD dwStyle = GetWindowLong( TreeView , GWL_STYLE);
        dwStyle |= TVS_CHECKBOXES;
        SetWindowLongPtr( TreeView , GWL_STYLE, dwStyle );

        /************ add items and subitems **********/

        // add root item

        TVINSERTSTRUCT tvis = {0};

        tvis.item.mask = TVIF_TEXT;
        tvis.item.pszText = L"This is root item";
        tvis.hInsertAfter = TVI_LAST;
        tvis.hParent = TVI_ROOT;

        HTREEITEM hRootItem = reinterpret_cast<HTREEITEM>( SendMessage( TreeView , 
            TVM_INSERTITEM, 0, reinterpret_cast<LPARAM>( &tvis ) ) );

        // and here is an example of removing a checkbox 
        // from a specific item/subitem in case you ever need it

        TVITEM tvi;
        tvi.hItem = hRootItem ;
        tvi.mask = TVIF_STATE;
        tvi.stateMask = TVIS_STATEIMAGEMASK;
        tvi.state = 0;
        TreeView_SetItem( TreeView, &tvi );

        // add firts subitem for the hTreeItem

        memset( &tvis, 0, sizeof(TVINSERTSTRUCT) );

        tvis.item.mask = TVIF_TEXT;
        tvis.item.pszText = L"This is first subitem";
        tvis.hInsertAfter = TVI_LAST;
        tvis.hParent = hRootItem;

        HTREEITEM hTreeSubItem1 = reinterpret_cast<HTREEITEM>( SendMessage( TreeView , 
            TVM_INSERTITEM, 0, reinterpret_cast<LPARAM>( &tvis ) ) );

        // now we insert second subitem for hRootItem

        memset( &tvis, 0, sizeof(TVINSERTSTRUCT) );

        tvis.item.mask = TVIF_TEXT | TVIF_STATE; // added extra flag
        tvis.item.pszText = L"This is second subitem";
        tvis.hInsertAfter = TVI_LAST;
        tvis.hParent = hRootItem;

        // for demonstration purposes let us check this node;
        // to do that add the following code, and add the extra flag for 
        // mask member like above
        tvis.item.stateMask = TVIS_STATEIMAGEMASK;
        tvis.item.state = 2 << 12;

        HTREEITEM hTreeSubItem2 = reinterpret_cast<HTREEITEM>( SendMessage( TreeView , 
            TVM_INSERTITEM, 0, reinterpret_cast<LPARAM>( &tvis ) ) );

        // let us expand the root node so we can see if checked state is really set
        TreeView_Expand( TreeView, hRootItem, TVE_EXPAND );
    }
    return 0L;

EDIT #2:

Here Is the part that explains how to check if item is checked ( it now properly checks when you click on a checkbox and when you press spacebar! ) :

case WM_NOTIFY:
    {
        LPNMHDR lpnmh = (LPNMHDR) lParam;

        if( lpnmh->idFrom == ID_TREE_VIEW  )  // if this is our treeview control
        {
            switch( lpnmh->code )  // let us filter notifications
            {
            case TVN_KEYDOWN:  // tree has keyboard focus and user pressed a key
                {

                    LPNMTVKEYDOWN ptvkd = (LPNMTVKEYDOWN)lParam; 

                    if( ptvkd->wVKey == VK_SPACE )  // if user pressed spacebar
                    {

                        // get the currently selected item
                        HTREEITEM ht = TreeView_GetSelection( ptvkd->hdr.hwndFrom );

                        // Prepare to test items state

                        TVITEM tvItem;

                        tvItem.mask = TVIF_HANDLE | TVIF_STATE;
                        tvItem.hItem = (HTREEITEM)ht;
                        tvItem.stateMask = TVIS_STATEIMAGEMASK;

                        // Request the information.
                        TreeView_GetItem( ptvkd->hdr.hwndFrom, &tvItem );

                        // Return zero if it's not checked, or nonzero otherwise.
                        if( (BOOL)(tvItem.state >> 12) - 1 )
                            MessageBox( hwnd, L"Not checked!", L"", MB_OK );
                        else
                            MessageBox( hwnd, L"Checked!", L"", MB_OK );

                    }
                }
                return 0L;  // see the documentation for TVN_KEYDOWN

            case NM_CLICK:  // user clicked on a tree
                {
                    TVHITTESTINFO ht = {0};

                    DWORD dwpos = GetMessagePos();

                    // include <windowsx.h> and <windows.h> header files
                    ht.pt.x = GET_X_LPARAM(dwpos);
                    ht.pt.y = GET_Y_LPARAM(dwpos);
                    MapWindowPoints( HWND_DESKTOP, lpnmh->hwndFrom, &ht.pt, 1 );

                    TreeView_HitTest(lpnmh->hwndFrom, &ht);

                    if(TVHT_ONITEMSTATEICON & ht.flags)
                    {
                        // Prepare to receive the desired information.

                        TVITEM tvItem;

                        tvItem.mask = TVIF_HANDLE | TVIF_STATE;
                        tvItem.hItem = (HTREEITEM)ht.hItem;
                        tvItem.stateMask = TVIS_STATEIMAGEMASK;

                        // Request the information.
                        TreeView_GetItem( lpnmh->hwndFrom, &tvItem );

                        // Return zero if it's not checked, or nonzero otherwise.
                        if( (BOOL)(tvItem.state >> 12) - 1 )
                            MessageBox( hwnd, L"Not checked!", L"", MB_OK );
                        else
                            MessageBox( hwnd, L"Checked!", L"", MB_OK );

                    }
                }
            default:
                break;
        }
    }
}
break;

The relevant idea for proper testing when spacebar is pressed is handling of TVN_KEYDOWN message.

We use this message to get NMTVKEYDOWN structure filled, which will give us virtual key code of the pressed button and the HWND of the treeview that sent the notification.

Now we use TreeView_GetItem() macro to get the currently selected node and we check its state the same way we did when we did hit testing.

My only problem is concerning this part from the documentation for TVN_KEYDOWN:

Return value

If the wVKey member of lParam is a character key code, the character will be used as part of an incremental search. Return nonzero to exclude the character from the incremental search, or zero to include the character in the search. For all other keys, the return value is ignored.

I just do not know what to do with the return result so I have put 0L.

Important note: If you need to return value from dialog box procedure use something like this:

SetWindowLongPtr( hwnd, DWLP_MSGRESULT, (LONG_PTR)1 );
return TRUE;

see the remarks for Return value in this documentation and use SetWindowLongPtr instead of SetWindowLong so you can support both x32 and x64 versions of Windows.

That would be all. Hopefully you have your problem solved. If you need further help leave a comment.

END OF EDIT

I have never done checking if tree item is checked but I believe that accepted answer to this question is the way to go.

NOTE:

I would highly appreciate if there someone who can provide code snippet for showing how to determine if treeview node is checked or not.

Community
  • 1
  • 1
AlwaysLearningNewStuff
  • 2,939
  • 3
  • 31
  • 84
  • +1. But your code has error. You have lost selection after using spacebar. – user2120666 Mar 17 '14 at 07:10
  • 1
    Thank You very much for a great and detailed answer. I have added code to my program and it works. Now only thing that bothers me is how to set some of checkboxes when program starts (programmaticaly) – user3213103 Mar 17 '14 at 10:09
  • I have tried adding bits to state variable before inserting items to TreeView like this: tvis.item.state = 1<<12; But, obviously I'm doing something wrong. – user3213103 Mar 17 '14 at 10:11
  • @user3213103: That link uses [example from Microsoft's site](http://msdn.microsoft.com/en-us/library/windows/desktop/hh270424%28v=vs.85%29.aspx). I have edited my answer in the `WM_CREATE` handler with a simpler way. After testing it I can confirm it works. If you need to display the node you can expand the parent programmatically by using [`TVM_EXPAND`](http://msdn.microsoft.com/en-us/library/windows/desktop/bb773568%28v=vs.85%29.aspx) message or [`TreeView_Expand`](http://msdn.microsoft.com/en-us/library/windows/desktop/bb773804%28v=vs.85%29.aspx) macro. Thanks for the accept. Best regards. – AlwaysLearningNewStuff Mar 17 '14 at 19:31
  • @user2120666: In order to restore the selection to the node ( after pressing spacebar and then closing the `MessageBox` ) you just need to add the following 2 lines of code **after `if/else` block**: `SetFocus( ptvkd->hdr.hwndFrom ); TreeView_Select( ptvkd->hdr.hwndFrom, tvItem.hItem, TVGN_CARET );` Now everything works the way you like. Hope this helps you. Best regards and thanks again for the upvote! – AlwaysLearningNewStuff Mar 17 '14 at 20:50
  • How would I now create parent item without checkbox, but that its child items have checkboxes? – user3213103 Mar 26 '14 at 23:03
  • I have edited my answer to help you. See the code in `WM_CREATE`, it is commented properly. [This question](http://stackoverflow.com/questions/17798463/is-it-possible-to-remove-some-checkboxes-from-tree-views-nodes) helped me, but there is a bug as I have mentioned in my edit. I will ask a question for solving it. Best regards. – AlwaysLearningNewStuff Mar 27 '14 at 08:47