Solved

Multi-line entries in ListBoxes & ComboBoxes

Posted on 1999-01-20
7
473 Views
Last Modified: 2013-11-20
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?
0
Comment
Question by:joeslow
  • 3
  • 3
7 Comments
 
LVL 12

Expert Comment

by:migel
ID: 1328045
Hi! You can use Ownerdraw LisBox and ComboBox.
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(lpMeasureItemStruct->itemID, str);
RECT rc = {0, 0, lpMeasureItemStruct->itemWidth, 0};
pDC->DrawText(str, str.GetLength(), &rc, DT_CALCRECT|DT_LEFT|DT_WORDBREAK);
lpMeasureItemStruct->itemHeight = rc.bottom;
pDC->SelectObject(pOldFont);
ReleaseDC(pDC);
}

// draw item
OnDrawItem( int nIDCtl, LPDRAWITEMSTRUCT 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);
lpMeasureItemStruct->itemHeight = rc.bottom;
dc.SelectObject(pOldFont);
dc.Detach();
}
0
 

Author Comment

by:joeslow
ID: 1328046
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::DrawItem(LPDRAWITEMSTRUCT 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->itemHeight = rc.bottom;
    dc.SelectObject(pOldFont);
    dc.Detach();
}

void CMultiLineListBox::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
    CFont * pFont = GetFont();
    CFont* pOldFont = 0;
    CString str;
    CDC* pDC = GetDC();
    pOldFont = pDC->SelectObject(pFont);
    GetText(lpMeasureItemStruct->itemID, str);
    //**** RECT rc = {0, 0, lpMeasureItemStruct->itemWidth, 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_WORDBREAK);
    lpMeasureItemStruct->itemHeight = rc.bottom;
    pDC->SelectObject(pOldFont);
    ReleaseDC(pDC);
}
0
 
LVL 12

Expert Comment

by:migel
ID: 1328047
to reflect state of the item(selected, focused...) you must check itemState of the DRAWITEMSTRUCT:
for example:

COLORREF crOldColor;
if (lpDrawItemStruct->itemState & ODS_SELECTED)
      {
      CBrush bkgnd(::GetSysColor(COLOR_HIGHLIGHT));
      dc.FillRect(&rect, &bkgnd);
      crOldColor = dc.SetTextColor(::GetSysColor(COLOR_HIGHLIGHTTEXT));
      }
else
              {
      CBrush bkgnd(::GetSysColor(COLOR_WINDOW));
      dc.FillRect(&lpDrawItemStruct->rcItem, &bkgnd);
      crOldColor = dc.SetTextColor(::GetSysColor(COLOR_WINDOWTEXT));
              }
//..Draw Text
// ... select old colors
      dc.SetTextColor(crOldColor);

      if (lpDrawItemStruct->itemState & ODS_FOCUS)
            dc.DrawFocusRect(&lpDrawItemStruct->rcItem);


0
Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

 

Author Comment

by:joeslow
ID: 1328048
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?
0
 
LVL 12

Accepted Solution

by:
migel earned 200 total points
ID: 1328049
Hi!
Just replace call GetWindowRect(&rc);  to the GetClientRect(&rc).
or change rc.right -= rc.left; rc.left = 0;
0
 

Author Comment

by:joeslow
ID: 1328050
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(LPDRAWITEMSTRUCT 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->itemState & ODS_SELECTED)
    {
        CBrush bkgnd(::GetSysColor(COLOR_HIGHLIGHT));
        dc.FillRect(&lpDrawItemStruct->rcItem, &bkgnd);
        crOldColor = dc.SetTextColor(::GetSysColor(COLOR_HIGHLIGHTTEXT));
        bkOldMode = dc.SetBkMode(TRANSPARENT);
    }
    else
    {
        CBrush bkgnd(::GetSysColor(COLOR_WINDOW));
        dc.FillRect(&lpDrawItemStruct->rcItem, &bkgnd);
        crOldColor = dc.SetTextColor(::GetSysColor(COLOR_WINDOWTEXT));
    }
    //..Draw Text
    dc.DrawText(str, str.GetLength(), &lpDrawItemStruct->rcItem, DT_LEFT|DT_WORDBREAK);

    if (lpDrawItemStruct->itemState & ODS_FOCUS)
    {
        dc.DrawFocusRect(&lpDrawItemStruct->rcItem);
    }

    // ... select old colors
    dc.SetTextColor(crOldColor);
    dc.SelectObject(pOldFont);
    dc.SetBkMode(bkOldMode);
    dc.Detach();
}

void CMLListBox::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
    CFont * pFont = GetFont();
    CFont* pOldFont = 0;
    CString str;
    CDC* pDC = GetDC();
    pOldFont = pDC->SelectObject(pFont);
    GetText(lpMeasureItemStruct->itemID, str);

    RECT rc;
    ::memset(&rc, 0, sizeof(RECT));
    GetClientRect(&rc);

    pDC->DrawText(str, str.GetLength(), &rc, DT_CALCRECT|DT_LEFT|DT_WORDBREAK);
    lpMeasureItemStruct->itemHeight = rc.bottom;
    pDC->SelectObject(pOldFont);
    ReleaseDC(pDC);
}

0
 

Expert Comment

by:talltk
ID: 21996854
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.
0

Featured Post

Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Title # Comments Views Activity
Excel Use VBA to get user's Mac Address for their computer 5 107
withoutTen challenge 14 97
sumHeights  challenge 17 65
changeXy challenge 13 67
Introduction: Hints for the grid button.  Nested classes, templated collections.  Squash that darned bug! Continuing from the sixth article about sudoku.   Open the project in visual studio. First we will finish with the SUD_SETVALUE messa…
Introduction: The undo support, implementing a stack. Continuing from the eigth article about sudoku.   We need a mechanism to keep track of the digits entered so as to implement an undo mechanism.  This should be a ‘Last In First Out’ collec…
This video will show you how to get GIT to work in Eclipse.   It will walk you through how to install the EGit plugin in eclipse and how to checkout an existing repository.
The Email Laundry PDF encryption service allows companies to send confidential encrypted  emails to anybody. The PDF document can also contain attachments that are embedded in the encrypted PDF. The password is randomly generated by The Email Laundr…

914 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

15 Experts available now in Live!

Get 1:1 Help Now