checkboxtreeviewc++builderc++builder-xe5

Builder C++ : TTreeView with checkbox items


I need to create a treeview with checkbox node like this image :

enter image description here

How to do that?

thx!


Solution

  • The TTreeView component does not natively support checkboxes, but the standard Windows TREEVIEW control does, via the TVS_CHECKBOXES style:

    TVS_CHECKBOXES

    Version 4.70. Enables check boxes for items in a tree-view control. A check box is displayed only if an image is associated with the item. When set to this style, the control effectively uses DrawFrameControl to create and set a state image list containing two images. State image 1 is the unchecked box and state image 2 is the checked box. Setting the state image to zero removes the check box altogether. For more information, see Working with state image indexes.

    Version 5.80. Displays a check box even if no image is associated with the item.

    Once a tree-view control is created with this style, the style cannot be removed. Instead, you must destroy the control and create a new one in its place. Destroying the tree-view control does not destroy the check box state image list. You must destroy it explicitly. Get the handle to the state image list by sending the tree-view control a TVM_GETIMAGELIST message. Then destroy the image list with ImageList_Destroy.

    If you want to use this style, you must set the TVS_CHECKBOXES style with SetWindowLong after you create the treeview control, and before you populate the tree. Otherwise, the checkboxes might appear unchecked, depending on timing issues.

    To apply the TVS_CHECKBOXES style to a TTreeView component, you should derive a new component and override the virtual CreateParams() method, eg:

    class TMyTreeView : public TTreeView
    {
    protected:
        virtual void __fastcall CreateParams(TCreateParams &Params);
    };
    
    void __fastcall TMyTreeView::CreateParams(TCreateParams &Params)
    {
        TTreeView::CreateParams(Params);
        Params.Style |= TVS_CHECKBOXES;
    }
    

    To assign the checkbox states in code, you can then use the TreeView_GetItem()/TreeView_SetItem() macros to toggle a node's state image index as needed.

    Alternatively, a more flexible approach is to simply assign your own TImageList to the TTreeView::StateImages property and fill it with whatever checkbox images you want, and then you can set the TTreeNode::StateIndex property as needed. To react to user input on the checkboxes, use the TTreeView::OnClick and TTreeView::OnKeyDown events to toggle the TTreeNode::StateIndex accordingly:

    void __fastcall ToggleTreeNodeCheckBox(TTreeNode *Node)
    {
        if ((Node) && (Node->StateIndex != -1))
        {
            if (Node->StateIndex == MyCheckedStateImageIndex)
                Node->StateIndex = MyUncheckedStateImageIndex;
            else
                Node->StateIndex = MyCheckedStateImageIndex;
        }
    }
    
    void __fastcall TMyForm::TreeView1Click(TObject *Sender)
    {
        TPoint P;
        ::GetCursorPos(&P);
        // or: P = Mouse->CursorPos;
        // or: POINTS pts = MAKEPOINTS(::GetMessagePos()); P = Point(pts.x, pts.y);
        P = TreeView1->ScreenToClient(P);
        if (TreeView1->GetHitTestInfoAt(P.x, P.y).Contains(htOnStateIcon))
            ToggleTreeNodeCheckBox(TreeView1->GetNodeAt(P.x, P.y));
    }
    
    void __fastcall TMyForm1::TreeView1KeyDown(TObject *Sender, WORD &Key, TShiftState Shift)
    {
        if (Key == VK_SPACE)
            ToggleTreeNodeCheckBox(TreeView1->Selected);
    }