Introduction
This simple class derived from CListBox. Its name is CMultiLineListBox. It is very simple to use, and can be added to your project like the standard CListBox control if you wish. Of course, it's stronger than the standard CListBox control, because this control supports multi-line text and foreground/background color set for each item in listbox.
Background
The standard CListBox control does not support multi-line and foreground/background color set. Default is single-line, foreground color is black and background color is white. In many cases, this standard control is not to be able to meet our needs, so we must extend it to provide more functions for us. Lucky, the CMultiLineListBox control can meet our needs. You will be able to display multi-line text and set foreground/background color for each item in listbox by using it.
Implementation
The CMultiLineListBox is derived from CListBox. Important, you must override DrawItem and MeasureItem virtual function. The two functions complete the main drawing operation. In addition, the custom member function - AppendString helps us to add text (multi-line, user "\r\n" to separate) and set foreground/background color.
- Deriving your class from
CListBox (or from some class derived rom CListBox)
- Overriding the
DrawItem and MeasureItem member function
- Adding and handling
WM_DESTORY message event
- Adding a custom member function -
AppendString in the class
In this class, I have a very important struct - _LISTBOX_COLOR_, this struct stores information for each item in listbox, including text and foreground/background color. The struct definition is like this:
typedef struct _LISTBOX_COLOR_
{
CString strText; COLORREF fgColor; COLORREF bgColor; _LISTBOX_COLOR_() {
strText.Empty();
fgColor = RGB(0, 0, 0);
bgColor = RGB(255, 255, 255);
}
}LISTBOX_COLOR, *PLISTBOX_COLOR;
Using the Code
AppendString: Custom member function, used to provide public interface for external call. This function has three parameters, text content and foreground/background color you set.
void CMultiLineListBox::AppendString(LPCSTR lpszText, COLORREF fgColor, COLORREF bgColor)
{
LISTBOX_COLOR* pInfo = new LISTBOX_COLOR;
pInfo->strText.Format(_T("%s"), lpszText);
pInfo->fgColor = fgColor;
pInfo->bgColor = bgColor;
SetItemDataPtr(AddString(pInfo->strText), pInfo);
}
DrawItem: Override virtual function, used to draw text and set foreground/background color for each item in listbox.
void CMultiLineListBox::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
ASSERT(lpDrawItemStruct->CtlType == ODT_LISTBOX);
LISTBOX_COLOR* pListBox = (LISTBOX_COLOR*)GetItemDataPtr(lpDrawItemStruct->itemID);
ASSERT(NULL != pListBox);
CDC dc;
dc.Attach(lpDrawItemStruct->hDC);
COLORREF crOldTextColor = dc.GetTextColor();
COLORREF crOldBkColor = dc.GetBkColor();
if ((lpDrawItemStruct->itemAction | ODA_SELECT) &&
(lpDrawItemStruct->itemState & ODS_SELECTED))
{
dc.SetTextColor(pListBox->bgColor);
dc.SetBkColor(pListBox->fgColor);
dc.FillSolidRect(&lpDrawItemStruct->rcItem, pListBox->fgColor);
}
else
{
dc.SetTextColor(pListBox->fgColor);
dc.SetBkColor(pListBox->bgColor);
dc.FillSolidRect(&lpDrawItemStruct->rcItem, pListBox->bgColor);
}
lpDrawItemStruct->rcItem.left += 5;
dc.DrawText(pListBox->strText, pListBox->strText.GetLength(), &lpDrawItemStruct->rcItem,
DT_WORDBREAK);
dc.SetTextColor(crOldTextColor);
dc.SetBkColor(crOldBkColor);
dc.Detach();
}
MeasureItem: Override virtual function, used to calculate current text height for each item in listbox.
void CMultiLineListBox::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
ASSERT(lpMeasureItemStruct->CtlType == ODT_LISTBOX);
CString strText(_T("�"));
GetText(lpMeasureItemStruct->itemID, strText);
ASSERT(TRUE != strText.IsEmpty());
CRect rect;
GetItemRect(lpMeasureItemStruct->itemID, &rect);
CDC* pDC = GetDC();
lpMeasureItemStruct->itemHeight = pDC->DrawText(strText, -1, rect,
DT_WORDBREAK | DT_CALCRECT);
ReleaseDC(pDC);
}
OnDestroy: WM_DESTROY message handler function, used to release memory resource allocated and avoid memory leaks.
void CMultiLineListBox::OnDestroy()
{
CListBox::OnDestroy();
int nCount = GetCount();
for(int i=0; i<nCount; i++)
{
LISTBOX_COLOR* pList = (LISTBOX_COLOR*)GetItemDataPtr(i);
delete pList;
pList = NULL;
}
}
How to Use
To integrate MultiLineListBox into your own project, you first need to add the following files to your project:
- MultiLineListBox.h
- MultiLineListBox.cpp
Now, here are two methods to use this control class - one is static associate, the other is dynamic create.
First, you will also need add ListBox control to dialog template. Next, include header file MultiLineListBox.h in dialog's h file, and create a CMultiLineListBox variable (or use Class Wizard to generate a variable for CListBox object, but the class name chooses CMultiLineListBox).
NOTE: This listbox must have styles - Owner draw variable and Has strings, NOT sort.
Finally, add the following code to OnInitDialog function in dialog.cpp file.
...
CString strText(_T("�"));
for(int i=0; i<sizeof(clr)/sizeof(clr[0]); i++)
{
if(i%2)
strText = _T("Hello, World!");
else
strText = _T("Hello, World!\r\nHello, World!");
m_listBox.AppendString(strText, clr[i][0], clr[i][1]);
}
...
The other way is dynamic create, use member function "Create" to generate CMultiLineListBox object.
NOTE: You must set LBS_OWNERDRAWVARIABLE and LBS_HASSTRINGS styles in Create function call.
First, you include header file MultiLineListBox.h in dialog's file. Next, create CMultiLineListBox variable (or use Class Wizard generate a variable for CListBox object, but the class name chooses CMultiLineListBox). Finally, add the following code to OnInitDialog function in dialog.cpp file.
#define IDC_LISTBOX 0x11 ...
CRect rc;
GetClientRect(&rc);
rc.bottom -= 35;
rc.DeflateRect(CSize(10, 10));
m_listBox.Create(WS_CHILD | WS_VISIBLE | WS_BORDER | WS_HSCROLL | WS_VSCROLL |
LBS_OWNERDRAWVARIABLE | LBS_HASSTRINGS, rc, this, IDC_LISTBOX);
CString strText(_T("�"));
for(int i=0; i<sizeof(clr)/sizeof(clr[0]); i++)
{
if(i%2)
strText = _T("Hello, World!");
else
strText = _T("Hello, World!\r\nHello, World!");
m_listBox.AppendString(strText, clr[i][0], clr[i][1]);
}
...
Of course, the color I set is not well, I believe you can do better than this. Now try it yourself. Good luck, thank you!
History
- 16th December, 2010: Initial post