Multi-Column Tabbed Checked List Box

CodeGuru content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.


Download Source Code
and Example

Checked list boxes are often used to show multiple choices that are from multiple-column
data source. You might want to try to put a tab between words to separate each item in the
list, like its parent, list box.

But if you set LBS_USETABSTOPS for your checked list box style and call SetTabStops()
function for your checked list box, all you get is some black marks where the tabs suppose
to be in your checked list box.

You might solve this problem by using fonts with constant stroke width (fixed-pitch),
like Pica, Elite, and Courier New, then set fixed length for the strings you add. If you
are not using these fonts in your checked list boxes, the class CTabCheckListBox is the
easiest way to do it.

The class, CTabCheckListBox, implements owner drawn checked list box derived from the
CCheckListBox, which overrides the CCheckListBox::DrawItem(), adds SetTabStops()
functions that are inherited from CListBox. You are going to use the same ways to setup
tab stops as CListBox do. The class CTabCheckListBox is simple, and using this class is
simple too, just like normal list box you use tabs, and follow the rules for building
normal checked list box.

The code of CTabCheckListBox::DrawItem() mostly comes from CCheckListBox::DrawItem()
by using TabbedTextOut() instead of ExtTextOut().


void CTabCheckListBox::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
ASSERT((GetStyle() & (LBS_OWNERDRAWFIXED | LBS_HASSTRINGS)) ==
(LBS_OWNERDRAWFIXED | LBS_HASSTRINGS));

CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);

if (((LONG)(lpDrawItemStruct->itemID) >= 0) &&
(lpDrawItemStruct->itemAction & (ODA_DRAWENTIRE | ODA_SELECT)))
{
int cyItem = GetItemHeight(lpDrawItemStruct->itemID);
BOOL fDisabled = !IsWindowEnabled() || !IsEnabled(lpDrawItemStruct->itemID);
COLORREF newTextColor = fDisabled ?
RGB(0x80, 0x80, 0x80) : GetSysColor(COLOR_WINDOWTEXT); // light gray
COLORREF oldTextColor = pDC->SetTextColor(newTextColor);
COLORREF newBkColor = GetSysColor(COLOR_WINDOW);
COLORREF oldBkColor = pDC->SetBkColor(newBkColor);

if (newTextColor == newBkColor)
newTextColor = RGB(0xC0, 0xC0, 0xC0); // dark gray

if (!fDisabled && ((lpDrawItemStruct->itemState & ODS_SELECTED) != 0))
{
pDC->SetTextColor(GetSysColor(COLOR_HIGHLIGHTTEXT));
pDC->SetBkColor(GetSysColor(COLOR_HIGHLIGHT));
}

if (m_cyText == 0)
VERIFY(cyItem >= CalcMinimumItemHeight());

CString strText;
GetText(lpDrawItemStruct->itemID, strText);

pDC->ExtTextOut(lpDrawItemStruct->rcItem.left,
lpDrawItemStruct->rcItem.top + max(0, (cyItem – m_cyText) / 2),
ETO_OPAQUE, &(lpDrawItemStruct->rcItem), “”, 0, NULL);

pDC->TabbedTextOut(lpDrawItemStruct->rcItem.left,
lpDrawItemStruct->rcItem.top + max(0, (cyItem – m_cyText) / 2),
strText, strText.GetLength(), m_nTabStops, m_lpnEachStop, lpDrawItemStruct->rcItem.left);

pDC->SetTextColor(oldTextColor);
pDC->SetBkColor(oldBkColor);
}

if ((lpDrawItemStruct->itemAction & ODA_FOCUS) != 0)
pDC->DrawFocusRect(&(lpDrawItemStruct->rcItem));
}

In order to use TabbedTextOut(), we need to convert dialog unit to logical unit
(or device unit). For the dialog box using the system font, ::GetDialogBaseUnits()
will do the work; I couldn’t find a exact way to get the dialog unit for using the
non-system font. But the code here does do the work we want.


int CTabCheckListBox::GetAverageCharWidths()
{
CFont* pFont = GetFont();
LOGFONT lf;
pFont->GetLogFont(&lf);
int nBaseUnit = lf.lfWidth;
if(nBaseUnit == 0)
nBaseUnit = LOWORD(GetDialogBaseUnits());
return nBaseUnit;
}

To use CTabCheckListBox:

  1. Add a list box control to your dialog resource. Setup the list box style by selecting LBS_OWNERDRAWFIXED, LBS_HASSTRINGS (without LBS_SORT selected). If you want to use the other checked list box styles, you are going to make your own class.

  2. Insert TabCheckListBox.cpp and TabCheckListBox.h files into your project.

  3. Add an instance of CTabCheckListBox in your dialog box member data.

  4. CTabCheckListBox m_ctrlCheckListBox;


  5. Call SubclassDlgItem() to initialize the class in your OnInitDialog();

  6. m_ctrlCheckListBox.SubclassDlgItem(IDC_LIST1, this);


  7. Set tab-stop position before you add strings to your checked list box. There are 3 functions to set the tab-stop, same as the CListBox::SetTabStops().

  8. void SetTabStops( );


    BOOL SetTabStops( const int& cxEachStop );


    BOOL SetTabStops( int nTabStops, LPINT rgTabStops );


    To set equal tab stops to the default size of 2 dialog units, call the parameterless version of this member function. To set equal tab stops to a size other than 2, call the version with the cxEachStop argument.


    To set tab stops to an array of sizes, use the version with the rgTabStops and nTabStops arguments. A tab stop will be set for each value in rgTabStops, up to the number specified by nTabStops.


    Referring CListBox::SetTabStops for how to using these functions.


  9. Add Strings to your checked list box, like


CString str;


str.Format(_T(“%s\t%s\t%d”), “aaZAaa”, “dddaaa”, 1);


m_ctrlCheckListBox.AddString(str);


The demo project shows how to use CTabCheckListBox by setting different tab stops
and using different font.

Last updated: 6 June 1998

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read