ComboBox with Tree Dropdown



Click here for a larger image.

Environment:VC6 SP4, NT4 SP3, Win98, Win2000

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 among 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:


  1. 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.
  2. Add all files named ComboTree*.cpp & .h and Subclass.cpp, Subclass.h, PupText.cpp, and PupText.h to your project.
  3. 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.
  4. In the header file for the dialog, add an include for ComboTree.h at the top. Manually add a ComboTree member variable, referred to below as m_ComboTree.
  5. 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.)

  6. Populate the control using the AddString() function.
  7. 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 doesn’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:.


NOTIFY_TREECOMBO_CHECK notification message for check event.
Message map handler example:
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.

16 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’s article for three-state checkboxes.

Downloads

Download demo project – 40 Kb

Download source – 30 Kb

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read