Click here to Skip to main content
15,867,453 members
Articles / Programming Languages / C++

Owner Drawn CListBox

Rate me:
Please Sign up or sign in to vote.
4.33/5 (9 votes)
16 Dec 2010CPOL3 min read 81.8K   7.4K   26   17
Owner drawn CListBox control, supports MultiLine, Foreground and Background color change
12.png

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.

  1. Deriving your class from CListBox (or from some class derived rom CListBox)
  2. Overriding the DrawItem and MeasureItem member function
  3. Adding and handling WM_DESTORY message event
  4. 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:

C++
// 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.

C++
// 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.

C++
// 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.

C++
// 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.

C++
// 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.

C++
// OnInitDialog
...
CString strText(_T("&#00;"));
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.

C++
#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("&#00;"));
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

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
China China
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionWhat has happened to the source code? Pin
hans.sch16-Sep-12 21:39
hans.sch16-Sep-12 21:39 
AnswerRe: What has happened to the source code? Pin
Terje196517-Sep-12 0:15
Terje196517-Sep-12 0:15 
GeneralRe: What has happened to the source code? Pin
hans.sch17-Sep-12 0:29
hans.sch17-Sep-12 0:29 
That did the trick. Thank you!

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.