For a project I have been working on for the last few months,
I wanted to add in a ListBox that would allow the user to select
a "block" of text instead of just a single line.
The resulting class CMultiLineListBox is a derived
class of CListBox. The original code for this class was taken
from the CColorListBox found in the Help documents of Visual C++
5.0 under the title "CTRLTEST Demo".
The actual ListBox must have the follow values set:
- Set the Ownerdraw to Variable.
- Make sure the chekbox for HAS_STRINGS is checked.
To use the class, follow these steps:
- Add a member variable to the desired class (CDialog,
CFormView, etc.)CMultiLineListBox m_List;
- Subclass the list box variable just created.
void CMultiLineDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CCharityReportDlg) DDX_Control(pDX, IDC_LIST, m_List); //}}AFX_DATA_MAP }
- To add an item value for listbox item nIndex, call
AddEntry( LPCTSTR lpszItem, COLORREF color, int nIndex );m_List.AddEntry( "Text" , RGB(255,255,0), 0);
The Code
At the moment, this class is very basic to look at code wise.
But since I havent seen anything like this on this website,I
figured I would share it. It is all done using only two
overrides. The first one being WM_MEASUREITEM. This allows the
class to get the chance to calculate the required height to make
each selection block and to set this value in the ListBox. The
following values allowed me to do the work required to do the
required calculations:
The following text was taken from
Visual C++ 5.0 Help documents:
DT_WORDBREAK | Specifies word-breaking. Lines are automatically broken between words if a word would extend past the edge of the rectangle specified by lpRect. A carriage return/linefeed sequence will also break the line. |
DT_CALCRECT | Determines the width and height of the rectangle. If there are multiple lines of text, DrawText will use the width of the rectangle pointed to by lpRect and extend the base of the rectangle to bound the last line of text. If there is only one line of text, DrawText will modify the right side of the rectangle so that it bounds the last character in the line. In either case, DrawText returns the height of the formatted text, but does not draw the text. |
void CMultiLineListBox::MeasureItem(LPMEASUREITEMSTRUCT lpMIS) { // all items are of fixed size // must use LBS_OWNERDRAWVARIABLE for this to work int nItem = lpMIS->itemID; CPaintDC dc(this); CString sLabel; CRect rcLabel; GetText( nItem, sLabel ); GetItemRect(nItem, rcLabel); // Calculate the required rectangle for the text and set the item height for this // specific item based on the return value (new height). int itemHeight = dc.DrawText( sLabel, -1, rcLabel, DT_WORDBREAK | DT_CALCRECT ); lpMIS->itemHeight = itemHeight; }
The second override was for the WM_DRAWITEM call:
void CMultiLineListBox::DrawItem(LPDRAWITEMSTRUCT lpDIS) { CDC* pDC = CDC::FromHandle(lpDIS->hDC); COLORREF rColor = (COLORREF)lpDIS->itemData; // RGB in item data CString sLabel; GetText(lpDIS->itemID, sLabel); // item selected if ((lpDIS->itemState & ODS_SELECTED) && (lpDIS->itemAction & (ODA_SELECT | ODA_DRAWENTIRE))) { // draw color box CBrush colorBrush(rColor); CRect colorRect = lpDIS->rcItem; // draw label background CBrush labelBrush(::GetSysColor(COLOR_HIGHLIGHT)); CRect labelRect = lpDIS->rcItem; pDC->FillRect(&labelRect,&labelBrush); // draw label text COLORREF colorTextSave; COLORREF colorBkSave; colorTextSave = pDC->SetTextColor(::GetSysColor(COLOR_HIGHLIGHTTEXT)); colorBkSave = pDC->SetBkColor(::GetSysColor(COLOR_HIGHLIGHT)); pDC->DrawText( sLabel, -1, &lpDIS->rcItem, DT_WORDBREAK ); pDC->SetTextColor(colorTextSave); pDC->SetBkColor(colorBkSave); return; } // item brought into box if (lpDIS->itemAction & ODA_DRAWENTIRE) { CBrush brush(rColor); CRect rect = lpDIS->rcItem; pDC->SetBkColor(rColor); pDC->FillRect(&rect,&brush); pDC->DrawText( sLabel, -1, &lpDIS->rcItem, DT_WORDBREAK ); return; } // item deselected if (!(lpDIS->itemState & ODS_SELECTED) && (lpDIS->itemAction & ODA_SELECT)) { CRect rect = lpDIS->rcItem; CBrush brush(rColor); pDC->SetBkColor(rColor); pDC->FillRect(&rect,&brush); pDC->DrawText( sLabel, -1, &lpDIS->rcItem, DT_WORDBREAK ); return; } }
And just to show how the AddEntry( LPCTSTR lpszItem, COLORREF
color, int nIndex ) code looks:
void CMultiLineListBox::AddEntry(LPCTSTR lpszItem, COLORREF color, int nIndex /* 0 */) { int index = InsertString(nIndex, lpszItem); SetItemData(index,color); }
This class can be easily extended to provide for text colors
& fonts. However at this time it was not required for the
project I am working on. Feel free to extend this as you require.
I have included both a sample project and a sample executable for
you to look at.
Download demo project and source – 20
KB