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..


struct LISTITEM
{
 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 crTextHighColor = GetSysColor(COLOR_HIGHLIGHTTEXT),
 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);
LISTITEM* GetItem(DWORD dwID);
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);
LISTITEM* GetItem(DWORD dwID);

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,
IDB_CHECK, IDB_UNCHECK, IDB_MIDDLECHECK ))
{
 return;
}

// 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() );

AfxMessageBox(csMessage);

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

AfxMessageBox(csMessage);
m_pCheckListStandard->SetTopIndex(6);

// Different ways to get an item
LISTITEM* pItem =
 m_pCheckListStandard->GetItem(
  m_pCheckListStandard->GetTopIndex());

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

// Get and Set the Item data
DWORD dwID =
 m_pCheckListStandard->GetItemData(
  m_pCheckListStandard->GetTopIndex());

csMessage.Format("The Data for this item is: %ld", dwID );
AfxMessageBox(csMessage);
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
m_pCheckListStandard->SetCurSel(6);
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)
m_pCheckListStandard->DeleteString(6);

// Delete eveything
m_pCheckListStandard->ResetContent();

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

Downloads

Download source – 18 Kb

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read