ComboTree
is a hybrid ComboBox
with a tree control replacing the drop down ListBox
. Implementing a hierarchy of choices using multiple dependant combo or list boxes can take up a lot of screen space and can be awkward to use. A dropdown tree control can be a good replacement. As an option, the ComboTree
supports a tree with three-state checkboxes for convenient multiple selection from a hierarchy of choices.
It has been tested and works on Win2000, Win98 and NT 4.0. Due to differences between these systems the technique of positioning a substitute control in place of the ListBox
of an existing ComboBox
upon receiving a CBN_DROPDOWN
message couldn't be relied on. Instead of subclassing and modifying the behavior, this control totally replaces the existing ComboBox
.
The previous version of ComboTree
used a tree control with the WS_POPUP
style that was a child of the ComboTree
's parent window. The WS_POPUP
style however caused the parent dialog to become inactive when the tree was dropped down. This version of ComboTree
creates the tree control as a child of the Desktop window like the ComboBox
does with its ListBox
. The ComboTree
subclasses the ComboTree
's parent window to intercept messages when the tree is dropped down and it also forwards keyboard messages to the tree. The built in tree control's tooltips did not work in this configuration, so the ComboTree
includes a custom tooltip control to provide them.
Here are the steps needed to use a ComboTree
in your project:
- Just as you normally would, place a
ComboBox
on the dialog. Set the dropdown height. The hybrid control will use the same sizes and positions as the original.
- Add all files named
ComboTree*.cpp & .h and Subclass.cpp, Subclass.h, PupText.cpp, PupText.h
to your project.
- If you will be using the checked tree style, import the bitmap file
ComboTreeCheckboxes.bmp
and give it a resource Id of "COMBOTREECHECKS"
(Note: The quotes are important! The quoted resource form is used to allow the bitmap to be optional.) Alternatively, you may open the resource file for the sample project, ComboTreeShow.rc
and drag and drop the "COMBOTREECHECKS" bitmap into your project's resource tab.
- In the header file for the dialog, add an include for
ComboTree.h
at the top. Manually add a ComboTree
member variable, referred to as m_ComboTree
below
- Create the
ComboTree
in the dialog's OnInitDialog()
function. If you are using checkboxes you must call SetHasCheckboxes(TRUE)
before creating the control. Create the ComboTree
by subclassing the ComboBox
from step one. Example syntax for creating a checked tree:
m_ComboTree.SetHasCheckboxes(TRUE);
m_ComboTree.SubclassDlgItem (IDC_COMBO_TREE);
(To create a tree without checkboxes, omit the SetHasCheckboxes()
call.)
- Populate the control using the
AddString()
function.
- Just as you normally would, use Class Wizard or edit manually to add handlers for the
ComboBox
notifications you want. ComboTree
uses the same CBN_*
notifications as a normal ComboBox
.
Usage Notes:
Important note: Don't use ClassWizard and the DDX/DDV mechanism to add and associate member variables with the ComboBox
. Once the original ComboBox
control is subclassed it won't exist anymore.
Not all
ComboBox
notifications are supported, but the following ones are:
ON_CBN_SELENDOK(IDC_COMBO_TREE, OnSelendokComboTree)
ON_CBN_SELENDCANCEL(IDC_COMBO_TREE, OnSelendcancelComboTree)
ON_CBN_KILLFOCUS(IDC_COMBO_TREE, OnKillfocusComboTree)
ON_CBN_SELCHANGE(IDC_COMBO_TREE, OnSelchangeComboTree)
ON_CBN_DBLCLK(IDC_COMBO_TREE, OnDblclkComboTree)
ON_CBN_SETFOCUS(IDC_COMBO_TREE, OnSetfocusComboTree)
ON_CBN_CLOSEUP(IDC_COMBO_TREE, OnCloseupComboTree)
ON_CBN_DROPDOWN(IDC_COMBO_TREE, OnDropdownComboTree)
When accessing the control, call the
ComboTree
functions directly instead of sending messges. The following access functions which emulate their
CComboBox
equivalents are supported.
void SetWindowText (LPCTSTR Text);
CString GetWindowText ();
int GetLBText (HTREEITEM hItem, CString& rText);
HTREEITEM AddString ( LPCTSTR lpszString);
HTREEITEM FindString ( LPCTSTR lpszString, HTREEITEM hParent = NULL);
HTREEITEM SelectString( LPCTSTR lpszString, HTREEITEM hParent = NULL);
HTREEITEM GetCurSel ();
int SetItemData (HTREEITEM hItem, DWORD dwItemData);
DWORD GetItemData (HTREEITEM hItem);
void ShowDropDown( BOOL bShowIt = TRUE );
void ResetContent ();
int SetDroppedWidth( UINT nWidth );
int GetDroppedWidth( );
BOOL GetDroppedState( );
int DeleteString( HTREEITEM hItem );
void GetDroppedControlRect (LPRECT pRect);
The access functions differ in a couple of important ways from normal CComboBox
functions. Functions that accept a string use the full path of the tree heirachy. For example to select the "Chipmunk" node use the full path to it:
hNode = m_ComboTree.SelectString ("Animals/Mammals/Chipmunk");
To populate the CComboBox
use the AddString ()
function. The function will add nodes for all levels of the path that don't already exist. The HTREEITEM
returned is for the end node. Example:
HTREEITEM hNode = m_ComboTree.AddString ("Plants/Trees/Oak");
ComboTree
functions accept or return a HTREEITEM
where the CComboBox
equivalent uses a ListBox
index. CB_ERR
is still used as an error return value in functions where the CComboBox
equivalent uses 0
as a success return value.
In addition to the CComboBox
emulation functions are the following extensions:.
ON_CONTROL (NOTIFY_TREECOMBO_CHECK, IDC_COMBO_TREE, OnComboTreeCheck)
CString GetTreePath (HTREEITEM hItem);
CString GetCurrentTreePath ();
TCHAR GetPathDelimiter ();
void SetPathDelimiter (TCHAR Delimiter);
int SetDroppedHeight (UINT nHeight);
int GetDroppedHeight ();
HTREEITEM FindChildItemData(DWORD SearchData, HTREEITEM hItem = NULL) ;
HTREEITEM FindChildItem (LPCTSTR Label, HTREEITEM hItem = NULL) ;
HTREEITEM GetLastItem( HTREEITEM hItem ) ;
HTREEITEM GetNextItem( HTREEITEM hItem ) ;
HTREEITEM GetPrevItem( HTREEITEM hItem ) ;
HTREEITEM GetLastSibling( HTREEITEM hItem ) ;
void CollapseBranch( HTREEITEM hItem) ;
void ExpandBranch( HTREEITEM hItem ) ;
void CollapseAllSiblings( HTREEITEM hNode );
BOOL SetHasCheckboxes (BOOL bHasCheckboxes);
BOOL GetHasCheckboxes ();
BOOL SetCheck( HTREEITEM hItem, BOOL bChecked = TRUE );
BOOL IsItemChecked(HTREEITEM hItem);
HTREEITEM GetFirstCheckedItem();
HTREEITEM GetNextCheckedItem( HTREEITEM hItem );
HTREEITEM GetPrevCheckedItem( HTREEITEM hItem );
GetTreePath () and GetCurrentTreePath()
return the text of the tree item prefixed with the concatenated text of its parent nodes.
Functions that contain "Check" in the name only are valid if the tree has a checked style
Three functions allow direct access to ComboTree
child controls.
ComboTreeDropList& GetTree () ;
ComboTreeEdit& GetEdit () ;
ComboTreeButton& GetDropDownButton ();
GetTree(), GetEdit () and GetDropDownButton()
allow direct access to the tree, edit and button controls. (Note: Use this direct access only in ways that do not conflict with the CBN_*
notifications and maintenance of control state.)
The ComboTree
doesn't have support for keyboard input to select tree items from within the edit control at this time. Text lookup similar to that of a drop down style combo box could be added with some additional search functionality. The edit control is currently read only.
History
- 8 Aug 2000 - Fixed painting bug reported by Steve Roach as well as a few other things I overlooked in emulating the droplist style ComboBox, like highlighting the control when it it gets focus, dropping down the tree when the edit gets a mouse click and disabling mouse and context menu text manipulation.
- 9 May 2003 - New version of
ComboTree
fixes all the known bugs, adds three-state checkbox. Thanks to all who reported them! Credits to Magerusan Grigore Cosmin's article for fix to dialog inactivation when tree is dropped down, Paul DiLascia's article for custom tooltips and Zafir Anjum article for three-state checkboxes.