joeslow
asked on
Multi-line entries in ListBoxes & ComboBoxes
I have combo boxes and list boxes that I fill with entries from a database. Some of these entries are very long. I have code that adjusts the width of the combobox droplist or list box extent to that of the longest line in it. Sometimes these are very wide and look kind of ackward. I was wondering if there was a way to have the text wrap to the next line in long strings. The highlighted entry then would have to be more than one line for these entries. Is this possible to do and if so, how?
ASKER
Thank you for your help. I've played with the code a little and had to make a few minor changes that I think work (See // ***)
I don't know how to set the selected item to highlighted though. If you could help me with that, I would greatly appreciate it.
void CMultiLineListBox::DrawIte m(LPDRAWIT EMSTRUCT lpDrawItemStruct)
{
CFont * pFont = GetFont();
CFont* pOldFont = 0;
CDC dc;
dc.Attach(lpDrawItemStruct ->hDC);
CString str;
pOldFont = dc.SelectObject(pFont);
GetText(lpDrawItemStruct-> itemID, str);
// select proper font and background colors according item state
// i can provide full code to do this
dc.DrawText(str, str.GetLength(), &lpDrawItemStruct->rcItem, DT_LEFT|DT_WORDBREAK);
// **** lpDrawItemStruct->itemHeig ht = rc.bottom;
dc.SelectObject(pOldFont);
dc.Detach();
}
void CMultiLineListBox::Measure Item(LPMEA SUREITEMST RUCT lpMeasureItemStruct)
{
CFont * pFont = GetFont();
CFont* pOldFont = 0;
CString str;
CDC* pDC = GetDC();
pOldFont = pDC->SelectObject(pFont);
GetText(lpMeasureItemStruc t->itemID, str);
//**** RECT rc = {0, 0, lpMeasureItemStruct->itemW idth, 0};
RECT rc;
GetWindowRect(&rc);
rc.left = 0;
rc.top = 0;
rc.bottom = 0;
pDC->DrawText(str, str.GetLength(), &rc, DT_CALCRECT|DT_LEFT|DT_WOR DBREAK);
lpMeasureItemStruct->itemH eight = rc.bottom;
pDC->SelectObject(pOldFont );
ReleaseDC(pDC);
}
I don't know how to set the selected item to highlighted though. If you could help me with that, I would greatly appreciate it.
void CMultiLineListBox::DrawIte
{
CFont * pFont = GetFont();
CFont* pOldFont = 0;
CDC dc;
dc.Attach(lpDrawItemStruct
CString str;
pOldFont = dc.SelectObject(pFont);
GetText(lpDrawItemStruct->
// select proper font and background colors according item state
// i can provide full code to do this
dc.DrawText(str, str.GetLength(), &lpDrawItemStruct->rcItem,
// **** lpDrawItemStruct->itemHeig
dc.SelectObject(pOldFont);
dc.Detach();
}
void CMultiLineListBox::Measure
{
CFont * pFont = GetFont();
CFont* pOldFont = 0;
CString str;
CDC* pDC = GetDC();
pOldFont = pDC->SelectObject(pFont);
GetText(lpMeasureItemStruc
//**** RECT rc = {0, 0, lpMeasureItemStruct->itemW
RECT rc;
GetWindowRect(&rc);
rc.left = 0;
rc.top = 0;
rc.bottom = 0;
pDC->DrawText(str, str.GetLength(), &rc, DT_CALCRECT|DT_LEFT|DT_WOR
lpMeasureItemStruct->itemH
pDC->SelectObject(pOldFont
ReleaseDC(pDC);
}
to reflect state of the item(selected, focused...) you must check itemState of the DRAWITEMSTRUCT:
for example:
COLORREF crOldColor;
if (lpDrawItemStruct->itemSta te & ODS_SELECTED)
{
CBrush bkgnd(::GetSysColor(COLOR_ HIGHLIGHT) );
dc.FillRect(&rect, &bkgnd);
crOldColor = dc.SetTextColor(::GetSysCo lor(COLOR_ HIGHLIGHTT EXT));
}
else
{
CBrush bkgnd(::GetSysColor(COLOR_ WINDOW));
dc.FillRect(&lpDrawItemStr uct->rcIte m, &bkgnd);
crOldColor = dc.SetTextColor(::GetSysCo lor(COLOR_ WINDOWTEXT ));
}
//..Draw Text
// ... select old colors
dc.SetTextColor(crOldColor );
if (lpDrawItemStruct->itemSta te & ODS_FOCUS)
dc.DrawFocusRect(&lpDrawIt emStruct-> rcItem);
for example:
COLORREF crOldColor;
if (lpDrawItemStruct->itemSta
{
CBrush bkgnd(::GetSysColor(COLOR_
dc.FillRect(&rect, &bkgnd);
crOldColor = dc.SetTextColor(::GetSysCo
}
else
{
CBrush bkgnd(::GetSysColor(COLOR_
dc.FillRect(&lpDrawItemStr
crOldColor = dc.SetTextColor(::GetSysCo
}
//..Draw Text
// ... select old colors
dc.SetTextColor(crOldColor
if (lpDrawItemStruct->itemSta
dc.DrawFocusRect(&lpDrawIt
ASKER
Now it seems to work pretty good. The only issue I have left is that when I call AddString("Some long string") during InitDialog it seems to add them fine. When I call the same function later, like on a button click, it adds only one line of the long string. I checked the rect calculated in MeasureItem's DrawText and it comes back with a height of only one line. What could be the problem?
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Migel,
Thank you for your help. It works great. The following is the code I ended up with. Do you see anything wrong with it? Also, what would be the differences (if any) for a ComboBox (instead of a ListBox)?
void CMLListBox::DrawItem(LPDRA WITEMSTRUC T lpDrawItemStruct)
{
CFont * pFont = GetFont();
CFont* pOldFont = 0;
CDC dc;
dc.Attach(lpDrawItemStruct ->hDC);
CString str;
pOldFont = dc.SelectObject(pFont);
GetText(lpDrawItemStruct-> itemID, str);
int bkOldMode;
COLORREF crOldColor;
if (lpDrawItemStruct->itemSta te & ODS_SELECTED)
{
CBrush bkgnd(::GetSysColor(COLOR_ HIGHLIGHT) );
dc.FillRect(&lpDrawItemStr uct->rcIte m, &bkgnd);
crOldColor = dc.SetTextColor(::GetSysCo lor(COLOR_ HIGHLIGHTT EXT));
bkOldMode = dc.SetBkMode(TRANSPARENT);
}
else
{
CBrush bkgnd(::GetSysColor(COLOR_ WINDOW));
dc.FillRect(&lpDrawItemStr uct->rcIte m, &bkgnd);
crOldColor = dc.SetTextColor(::GetSysCo lor(COLOR_ WINDOWTEXT ));
}
//..Draw Text
dc.DrawText(str, str.GetLength(), &lpDrawItemStruct->rcItem, DT_LEFT|DT_WORDBREAK);
if (lpDrawItemStruct->itemSta te & ODS_FOCUS)
{
dc.DrawFocusRect(&lpDrawIt emStruct-> rcItem);
}
// ... select old colors
dc.SetTextColor(crOldColor );
dc.SelectObject(pOldFont);
dc.SetBkMode(bkOldMode);
dc.Detach();
}
void CMLListBox::MeasureItem(LP MEASUREITE MSTRUCT lpMeasureItemStruct)
{
CFont * pFont = GetFont();
CFont* pOldFont = 0;
CString str;
CDC* pDC = GetDC();
pOldFont = pDC->SelectObject(pFont);
GetText(lpMeasureItemStruc t->itemID, str);
RECT rc;
::memset(&rc, 0, sizeof(RECT));
GetClientRect(&rc);
pDC->DrawText(str, str.GetLength(), &rc, DT_CALCRECT|DT_LEFT|DT_WOR DBREAK);
lpMeasureItemStruct->itemH eight = rc.bottom;
pDC->SelectObject(pOldFont );
ReleaseDC(pDC);
}
Thank you for your help. It works great. The following is the code I ended up with. Do you see anything wrong with it? Also, what would be the differences (if any) for a ComboBox (instead of a ListBox)?
void CMLListBox::DrawItem(LPDRA
{
CFont * pFont = GetFont();
CFont* pOldFont = 0;
CDC dc;
dc.Attach(lpDrawItemStruct
CString str;
pOldFont = dc.SelectObject(pFont);
GetText(lpDrawItemStruct->
int bkOldMode;
COLORREF crOldColor;
if (lpDrawItemStruct->itemSta
{
CBrush bkgnd(::GetSysColor(COLOR_
dc.FillRect(&lpDrawItemStr
crOldColor = dc.SetTextColor(::GetSysCo
bkOldMode = dc.SetBkMode(TRANSPARENT);
}
else
{
CBrush bkgnd(::GetSysColor(COLOR_
dc.FillRect(&lpDrawItemStr
crOldColor = dc.SetTextColor(::GetSysCo
}
//..Draw Text
dc.DrawText(str, str.GetLength(), &lpDrawItemStruct->rcItem,
if (lpDrawItemStruct->itemSta
{
dc.DrawFocusRect(&lpDrawIt
}
// ... select old colors
dc.SetTextColor(crOldColor
dc.SelectObject(pOldFont);
dc.SetBkMode(bkOldMode);
dc.Detach();
}
void CMLListBox::MeasureItem(LP
{
CFont * pFont = GetFont();
CFont* pOldFont = 0;
CString str;
CDC* pDC = GetDC();
pOldFont = pDC->SelectObject(pFont);
GetText(lpMeasureItemStruc
RECT rc;
::memset(&rc, 0, sizeof(RECT));
GetClientRect(&rc);
pDC->DrawText(str, str.GetLength(), &rc, DT_CALCRECT|DT_LEFT|DT_WOR
lpMeasureItemStruct->itemH
pDC->SelectObject(pOldFont
ReleaseDC(pDC);
}
Hi,
I'm an amateur reading this solution. I don't know where to ask this but, here it goes:
What class is GetText function in? I keep getting compiler errors when I call GetText(). I added the OnMeasureItem and OnDrawItem in my Dialog class with a member variable m_combobox.
If I try to replace GetText with m_combobox.GetLBText(), I also get errors. Please help.
I'm an amateur reading this solution. I don't know where to ask this but, here it goes:
What class is GetText function in? I keep getting compiler errors when I call GetText(). I added the OnMeasureItem and OnDrawItem in my Dialog class with a member variable m_combobox.
If I try to replace GetText with m_combobox.GetLBText(), I also get errors. Please help.
Just derive your own class from the MFC CListBox(CComboBox) and override OnMeasureItem and OnDrawItem methods.
OnMeasureItem method is very important for your task. In this method you must define height of the each item in the list(combo).
for example :// in Rc file list must be defined with LBS_OWNERDRAWVARIABLE style
OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
CFont * pFont = GetFont();
CFont* pOldFont = 0;
CString str;
CDC* pDC = GetDC();
pOldFont = pDC->SelectObject(pFont);
GetText(lpMeasureItemStruc
RECT rc = {0, 0, lpMeasureItemStruct->itemW
pDC->DrawText(str, str.GetLength(), &rc, DT_CALCRECT|DT_LEFT|DT_WOR
lpMeasureItemStruct->itemH
pDC->SelectObject(pOldFont
ReleaseDC(pDC);
}
// draw item
OnDrawItem( int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct )
{
CFont * pFont = GetFont();
CFont* pOldFont = 0;
CDC dc;
dc.Attach(lpDrawItemStruct
CString str;
pOldFont = dc.SelectObject(pFont);
GetText(lpDrawItemStruct->
// select proper font and background colors according item state
// i can provide full code to do this
dc.DrawText(str, str.GetLength(), &lpDrawItemStruct->rcItem,
lpMeasureItemStruct->itemH
dc.SelectObject(pOldFont);
dc.Detach();
}