Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / MFC

CQuadStateTree

4.88/5 (16 votes)
2 Dec 2014CPOL6 min read 39.8K   1.7K  
A CTreeCtrl derived control that has four checkbox states

Quad State Tree Control

Introduction

I was in need of a tree control with several features that are not found in the standard CTreeCtrl:

  1. Checkboxes with indeterminate state. When a tree item's child items are not all in the same state then that item's checkbox should show an indeterminate state.
  2. Setting a parent tree item's checkbox state to checked or unchecked should automatically set all its child tree item's states to the same state.
  3. Setting a child tree item's checkbox state to checked or unchecked should automatically set its parent tree item's state to the appropriate state.
  4. Not all the items in the tree need to have a checkboxes. Sometimes, the root of the tree just needs to be a labeled place holder.
  5. Notifications. It would be really nice if the parent window of the tree control recieved detailed notification messages when a tree item's checkbox state changes.
  6. Ability to block a change in a checkbox state. When a notification (see above) is received by the parent window, it should be possible to block the checkbox state from changing.

Presented in this article is the CQuadStateTree class that delivers all these requirements, plus a few more.

Background

Why Quad State you may ask? It is because each checkbox in the tree control can have four states. Of course, there is the basic checked and unchecked, and the familiar indeterminate state. But I have added a forth state which is 'none'. You may say that 'none' is not a state, but it actually is. How else would one specify that a tree item not have a checkbox when the rest of the tree control does have them?

Of course to have the 'none' state make any sense there has to be a basic restriction placed on it. The restriction being that in order for a tree item to have a 'none' state, all of its parent items up the tree, all the way up to the root, must also be in the 'none' state. It makes no sense to able to select a parent item and not be able to select a child item.

Using the Code

The first step for using this code in your own projects is to add the QuadStateTree.h and QuadStateTree.cpp files to your project. Then in any file that needs access to the CQuadStateTree class, you just have to #include QuadStateTree.h.

Since CQuadStateTree is derived from CTreeCtrl, you can use this class the exact same way you would use CTreeCtrl with the following API exceptions:

CQuadStateTree Data Types

The CQuadStateTree uses the following custom data types.

TVCS_CHECKSTATE

The TVCS_CHECKSTATE enum defines the four possible checkbox states.

Syntax

C#
enum TVCS_CHECKSTATE
{
    TVCS_NONE          = -1,
    TVCS_UNCHECKED     =  0,
    TVCS_CHECKED       =  1,
    TVCS_INDETERMINATE =  2 
};

Members

TVCS_NONE The tree item has no checkbox associated with it.
TVCS_UNCHECKED The tree item's checkbox does not have a check mark in it.
TVCS_CHECKED The tree item's checkbox has a check mark in it.
TVCS_INDETERMINATE The tree item has child items, and the child items are a mix of checked and unchecked.

NMTVNCHECK

The NMTVNCHECK structure contains information about a change in a tree item's checkbox state.

Syntax

C#
typedef struct tagTVNCHECK    
{
    NMHDR hdr;                     
    HTREEITEM hTreeItem;           
    LPARAM lParam;                 
    TVCS_CHECKSTATE OldCheckState; 
    TVCS_CHECKSTATE NewCheckState; 
    HTREEITEM TriggerItem;         
} NMTVNCHECK, *LPNMTVNCHECK;

Members

hdr NMHDR structure that contains information about the TVN_CHECK notification message.
hTreeItem Handle to the tree item whose checkbox is changing state.
lParam Application specific data that is associated with hTreeItem.
OldCheckState The old checkbox state for hTreeItem.
NewCheckState The new checkbox state for hTreeItem.
TriggerItem Handle to the tree item that was toggled by the user or was specified in a call to SetCheck().

CQuadStateTree Notification Messages

TVN_CHECK notification code

The TVN_CHECK notification is sent to the parent window as a WM_NOTIFY message to inform the parent that a checkbox is about to change state.

Syntax

C#
TVN_CHECK

    pNMTvnCheck = (LPNMTVNCHECK) lParam

Parameters

pNMTvnCheck a pointer to a NMTVNCHECK structure.

Return Value

If the hTreeItem and TriggerItem members of the NMTVNCHECK structure are pointing to the same tree item then returning a non-zero value will stop the tree item checkbox from changing.

In all other cases, the return value is ignored.


ON_TVN_CHECK Message map macro

Use the ON_TVN_CHECK macro to define an TVN_CHECK notification handler function.

Syntax

C#
ON_TVN_CHECK(<nID>, <pFunction>)

Parameters

nID Identification number for the tree control
pFunction Pointer to the member function to be called when the TVN_CHECK notification is sent

Remarks

pFunction must be declared with the following prototype:

C#
afx_msg void memberFxn( NMHDR * pNMHDR, LRESULT * pResult );

where the italicized parameters are:

pNMHDR
A pointer to a NMHDR structure which can be cast to a LPNMTVNCHECK structure pointer.

pResult
A pointer to the result code that you set before you return.


CQuadStateTree Member Functions

CQuadStateTree::CQuadStateTree

Class constructor that is called to construct a CQuadStateTree object.

Syntax

C#
CQuadStateTree();

CQuadStateTree::~CQuadStateTree

Class destructor that is called when a CQuadStateTree object is deleted.

Syntax

C#
virtual ~CQuadStateTree();

CQuadStateTree::GetCheck

Syntax

C#
TVCS_CHECKSTATE GetCheck(
    HTREEITEM hTreeItem) const;

Parameter

hTreeItem Handle to the tree item to retrieve the checkbox state from.

Return Value

Returns a TVCS_CHECKSTATE enum indicating the current state of the checkbox.

Remarks

This function is identical to CTreeCtrl::GetCheck. The only difference is that the return value is returned as a TVCS_CHECKSTATE enum instead of a BOOL.


CQuadStateTree::SetCheck

Syntax

C#
BOOL SetCheck (
    HTREEITEM hTreeItem,
    TVCS_CHECKSTATE NewCheckState = TVCS_CHECKED);

Parameters

hTreeItem Handle to the tree item to receive the checkbox state change.
NewCheckState The new checkbox state. By default, the checkbox state is set to TVCS_CHECKED.

Return Value

Nonzero if successful; otherwise zero.

Remarks

SetCheck() will return zero for the following reasons:

  1. NewCheckState is TVCS_INDETERMINATE. The TVCS_INDETERMINATE state will only be set by the control when a tree item's children are in different states. It is not possible to set TVCS_INDETERMINATE via SetCheck().
  2. NewCheckState is TVCS_NONE. The only way to set a checkbox to TVCS_NONE state is if all hTreeItem's parent tree items are already set to TVCS_NONE. To set TVCS_NONE state you have to start at the tree's root item and work your way down.

CQuadStateTree::Create

Call this function to create a control (a child window) and associate it with the CQuadStateTree object.

Syntax

C#
virtual BOOL Create(
    DWORD dwStyle,
    const RECT& rect,
    CWnd* pParentWnd,
    UINT nID);

Parameters

dwStyle Specifies the tree view control's style. Apply window styles, described in CreateWindow, and any combination of tree view control styles as described in the Platform SDK.
rect A reference to a RECT structure describing the size and position of the window to be created, in client coordinates of pParentWnd.
pParentWnd A pointer to the window that is the control's parent.
nID The control's child-window ID.

CQuadStateTree::CreateEx

Call this function to create a control (a child window) and associate it with the CQuadStateTree object.

Syntax

C#
virtual BOOL CreateEx(
    DWORD dwExStyle,
    DWORD dwStyle,
    const RECT& rect,
    CWnd* pParentWnd,
    UINT nID);

Parameters

dwExStyle Specifies the extended style of the control being created. For a list of extended Windows styles, see the dwExStyle parameter for CreateWindowEx in the Platform SDK.
dwStyle Specifies the tree view control's style. Apply window styles, described in CreateWindow, and any combination of tree view control styles as described in the Platform SDK.
rect A reference to a RECT structure describing the size and position of the window to be created, in client coordinates of pParentWnd.
pParentWnd A pointer to the window that is the control's parent.
nID The control's child-window ID.

Remarks

I have not explored the tree view extended styles, so I have no idea how they will influence this control.


Deriving from CQuadStateTree

CQuadStateTree has to handle several different messages in order to work properly. If you derive your own control from CQuadStateTree and you handle the same messages in derived class, be sure to call the CQuadStateTree message handler functions from your message handler functions.

Listed here is the messages that CQuadStateTree handles and the name of the functions that handle them:

NM_CLICK CQuadStateTree::OnNMClick(NMHDR *pNMHDR, LRESULT *pResult)
NM_TVSTATEIMAGECHANGING CQuadStateTree::OnNMTvStateImageChanging(NMHDR *pNMHDR, LRESULT *pResult)
TVN_KEYDOWN CQuadStateTree::OnTvnKeydown(NMHDR *pNMHDR, LRESULT *pResult)
TVN_ITEMCHANGED CQuadStateTree::OnTvnItemchanged(NMHDR *pNMHDR, LRESULT *pResult)
TVM_SETITEM CQuadStateTree::OnTvmSetitem(WPARAM wp, LPARAM lp)

History

  • November 28, 2014 - Initial release

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)