Owner Drawn CListBox






4.33/5 (9 votes)
Owner drawn CListBox control, supports MultiLine, Foreground and Background color change

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 romCListBox
) - Overriding the
DrawItem
andMeasureItem
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:
// This struct store information for each item in listbox
typedef struct _LISTBOX_COLOR_
{
CString strText; // text content, default value is _T("")
COLORREF fgColor; // foreground color, default color is black
COLORREF bgColor; // background color, default color is white
_LISTBOX_COLOR_() // constructor
{
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.
// Custom member function. Append string and set foreground and
// background color for each item in listbox
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
.
// DrawItem virtual function, draw text and color
void CMultiLineListBox::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
// TODO: Add your code to draw the specified item
ASSERT(lpDrawItemStruct->CtlType == ODT_LISTBOX);
LISTBOX_COLOR* pListBox = (LISTBOX_COLOR*)GetItemDataPtr(lpDrawItemStruct->itemID);
ASSERT(NULL != pListBox);
CDC dc;
dc.Attach(lpDrawItemStruct->hDC);
// Save these value to restore them when done drawing.
COLORREF crOldTextColor = dc.GetTextColor();
COLORREF crOldBkColor = dc.GetBkColor();
// If this item is selected, set the background color
// and the text color to appropriate values. Also, erase
// rect by filling it with the background color.
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;
// Draw the text.
dc.DrawText(pListBox->strText, pListBox->strText.GetLength(), &lpDrawItemStruct->rcItem,
DT_WORDBREAK);
// Reset the background color and the text color back to their
// original values.
dc.SetTextColor(crOldTextColor);
dc.SetBkColor(crOldBkColor);
dc.Detach();
}
MeasureItem: Override virtual function, used to calculate current text height for each item in listbox
.
// MeasureItem virtual function, calculate text height
void CMultiLineListBox::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
// TODO: Add your code to determine the size of specified item
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.
// WM_DESTROY handler, delete new resource
void CMultiLineListBox::OnDestroy()
{
CListBox::OnDestroy();
// TODO: Add your message handler code here
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.
// OnInitDialog
...
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 // define resource ID
// OnInitDialog
...
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