A Multi-Level CCheckListbox

Recently, I was in the need of a control that allowed the users the ability to check multiple items in a CListbox type of fashion. I would of used the common MFC control CCheckListBox but I needed the items to be presented in a hierarchy and be totally customizable (font, colors, images, etc.) for each individual item. I also thought about using the MFC CTreeCtrl and do my own custom drawing but I really wanted the CListbox look and feel along without the ability to expand and collapse each item. So...here's my solution...

The interface's supplied to use this class is as follows (examples to use them are below

Each item added to the control is represented by the following structure..

 LISTITEM* pParentItem; // Points to its parent
 int nCheckedState; // It check state

 // Total width of this item (includes bitmap & indents)
 int nTotalWidth;

 int nTextLength; // Length of the text in bytes
 int nLevel;   // How many tabs it is over
 // Is this item selected (multiple selection eventually)
 bool bSelected;

 DWORD dwID;   // Id accociated with the item
 CString csText;   // Text of the item

 // Appearence
 COLORREF crTextColor; // Text color
 COLORREF crTextHighColor; // Text selection color
 COLORREF crBgHighlightColor; // Background highlight color
 // List to other child list items
 CPtrList m_ItemList;  // List of all this items children
You can obtain this structure for every item in the control to get and set its attributes. Be careful not to change certian items though, such as nLevel and nTextLength. (these are for internal use only!)

User Functions

BOOL Create( CRect Rect, CWnd* pParent, UINT uID,
 UINT nCheckBitmap, 
 UINT nUnCheckBitmap, 
 UINT nMiddleCheckBitmap,
 COLORREF crBkColor = 
  GetSysColor(COLOR_WINDOW), CFont* pCustomFont = NULL);

LISTITEM* AddString(  CString csText,
LISTITEM* pParentItem = NULL,
 int nCheckState = UNCHECKED,
 DWORD dwID = -1,  
 COLORREF crTextColor = GetSysColor(COLOR_INFOTEXT),
 COLORREF crBgHighlightColor = GetSysColor(COLOR_HIGHLIGHT));

int  DeleteString(int nItem);  
int  GetCount(LISTITEM* pParentItem = NULL);
int  GetTopIndex();
int  SetTopIndex(int nTop);
DWORD GetItemData(int nItem);
int  SetItemData(int nItem, DWORD dwID);
LISTITEM* GetItem(int nItem);
int  GetText(int nItem, CString* pString );  
int  GetTextLen(int nItem );  
int  GetCurSel();
int  SetCurSel(int nItem);
int  SetCheck(int nItem, int nCheckState );
int  GetCheck(int nItem);
void  ResetContent();
Most of the above functions work identical to the standard CListBox functions so I will spare you the verbiage in how to use them. I will however, elaborate on some specific ones that are new to this class or work a little differently then the norm.

BOOL Create( CRect Rect, CWnd* pParent, UINT uID, ...)
In the create call you must specify the three bitmaps you want to be used to display the states of each item. These imaged must be the same exact size! You can also see that the background color can be set along with the font to use.

LISTITEM* AddString( CString csText, ...)
In the AddString call, you can set the items parent (by using the item that is returned in a previous AddString call) You can also set a lot of custom features such as: State, ID, and lots of colors!

int DeleteString(int nItem);
Removes the item sent, PLUS all of the items children, and their children, etc.

int GetCount(LISTITEM* pParentItem = NULL);
Returns the total items in the control if NULL is sent in or the number of children for a specific item.

LISTITEM* GetItem(int nItem);
Both of these functions find an item based on either their positions in the control or the ID set for the them. As you can see, it returns the entire structure that represents the item.

There are other inline functions that are also available. These are (hopefully) self-explanatory.

inline int   GetWidestItem()  { return m_nWidestItem; };
inline int   GetLineHeight()  { return m_nLineHeight; };
inline CPen*  GetBkPen()   { return m_pBkPen; };
inline CBrush*  GetBkBrush()  { return m_pBkBrush; };
inline CFont*  GetTextFont()  { return m_pTextFont; };
inline int   GetImageWidth()  { return m_cBitmapDimen.cx; };
inline int   GetImageHeight() { return m_cBitmapDimen.cy; };
inline CBitmap*  GetCheckImage()  { return m_pCheck; };
inline CBitmap*  GetUnCheckImage() { return m_pUnCheck; };
inline CBitmap*  GetMiddleImage() { return m_pMiddleCheck; };
Here's some sample code in how to fill and manipulate items in the control...

// Create the Control
LISTITEM* pParentItem = NULL;
m_pCheckList = new CCheckList();
if( !m_pCheckList->Create( CRect(50,50,300,300), this, 10001,

// Add some items

// Root Item
pParentItem = m_pCheckListStandard->AddString("Exotics"); 

// Sub Item
m_ pCheckList ->AddString("Lamborghini", pParentItem );  

// Sub Item
m_ pCheckList ->AddString("Corvette", pParentItem );  

// Sub Item
m_ pCheckList ->AddString("Vector", pParentItem );   

// Sub Item
m_ pCheckList ->AddString("Hummer", pParentItem );  

//  Sub-Sub Item
pParentItem = m_ pCheckList ->AddString("Porsche", pParentItem ); 

//  Sub-Sub Item
m_ pCheckList ->AddString("Boxster", pParentItem );  

//  Sub-Sub Item
m_ pCheckList ->AddString("928 S4", pParentItem);   

//  Sub-Sub Item
m_ pCheckList ->AddString("959", pParentItem );   
// Root Item
m_ pCheckList ->AddString("Luxury");     

// Root Item
m_ pCheckList ->AddString("Trucks");    

// Root Item
m_ pCheckList ->AddString("Sport Utility Vehicles");  

// Root Item
m_ pCheckList ->AddString("Classics");    

// Function uses
// Get Total Count
CString csMessage;
csMessage.Format("There are a total of %d items", 
 m_pCheckListStandard->GetCount() );


// Get and Set the top index
csMessage.Format("The top index is: %d", 
 m_pCheckListStandard->GetTopIndex() );


// Different ways to get an item
LISTITEM* pItem = 

pItem = m_pCheckList->GetItem((DWORD)5000);

// Get and Set the Item data

csMessage.Format("The Data for this item is: %ld", dwID );
m_pCheckListStandard->SetItemData(6, 2000);

// Get the text and text length
CString csText;
m_pCheckListStandard->GetText( 6, &csText );
int nTemp = m_pCheckListStandard->GetTextLen( 7 );

// Get and set the current selection
nTemp = m_pCheckListStandard->GetCurSel();

// Get and Set Checks
m_pCheckListStandard->SetCheck(6, CHECKED);
nTemp = m_pCheckListStandard->GetCheck(6);

// Remove one item and all its subitems (if it has them)

// Delete eveything

I have included the standard looking three states for checkboxes..but feel free to think outside the box and create your own!


Download source - 18 Kb


  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Moving from an on-premises environment to Office 365 does not remove the need to plan for disruptions or reduce the business risk requirements for protecting email services. If anything, some risks increase with a move to the cloud. Read how to ease the transition every business faces if considering or already migrating to cloud email. This white paper discusses: Setting expectations when migrating to Office 365 Understanding the implications of relying solely on Exchange Online security Necessary archiving …

  • By providing developers with the right tools to detect, understand, and fix problems early, your business can simplify software development, shorten development lifecycles, and improve the quality of software code. The end result is increased innovation, secure applications, and a faster time to market — all at a lower cost.

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date