蝉虞世南 拼音版:在listbox里显示多行文本2

来源:百度文库 编辑:九乡新闻网 时间:2024/04/30 00:58:06

Owner Drawn CListBox - Version 2

By Visual-Eleven |14 Feb 2011| Unedited contributionC++WindowsVisual-StudioMFCAn owner drawn CListBox, expands items to look like a CTreeCtrl See Also
  • More like this
  • More by this author
Print Article Twitter Digg Facebook Del.icio.us Reddit Stumbleupon Newsvine Technorati Mr. Wong Yahoo! Google Windows Live Send as Email Add to your CodeProject bookmarksDiscuss this article1 Article Browse Code Stats Revisions (2)   4.67 (3 votes)
1
2
3
4
5 Sponsored LinksIs your email address OK?You are signed up for our newsletters but your email address is either unconfirmed, or has notbeen reconfirmed in a long time. Please clickhere to have aconfirmation email sent so we can confirm your email address and start sending you newslettersagain. Alternatively, you canupdate your subscriptions.
  • Download Source - 149.03 KB
  • Download Demo Executable - 81.16 KB

Introduction

This is a little similar to the previous article whose link is here. The difference is, this CMultiLineListBox class supports dynamic multi-line display. When user clicks or chooses an item in the ListBox, the item will be expanded to show more information. Its look like a CTreeCtrl control.

Implementation

The CMultiLineListBox is derived from CListBox. Important: you must override DrawItem and MeasureItem virtual functions. The two functions complete the main drawing operation. In addition, it handles window messages like WM_ERASEBKGND, WM_KEYDOWN, WM_LBUTTONDOWN, WM_MOUSEMOVE and custom messages MSG_UPDATEITEM. The MSG_UPDATEITEM message is posted when the user clicks an item, drags the mouse or presses a direction key (up / down keys).

In this class, we define an important struct named LISTBOX_INFO. this struct stores information for each item in ListBox. The struct definition is like this:

Collapse
// This struct store information for each item in ListBox    typedef struct _LISTBOX_INFO_    {    public:    typedef struct _SUBNODE_INFO_ // Subnode properties     {    public:    CString strText; // text content, default value is _T("")      COLORREF fgColor; // foreground color, default color is black      COLORREF bgColor; // background color, default color is white      _SUBNODE_INFO_() // constructor      {    clean();    }    ~_SUBNODE_INFO_() // destructor      {    clean();    }    protected:    inline void clean(void) // inline function used to initialize member variable      {    strText.Empty();    fgColor = RGB_FOREGROUND;    bgColor = RGB_BACKGROUND;    }    }SUBNODEINFO, *PSUBNODEINFO;    public:    vector subArray; // Node properties, pre item maybe include many of subnode     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_INFO_()  // constructor     {    clean();    }    ~_LISTBOX_INFO_()  // destructor     {    clean();    }    protected:    inline void clean(void)  // inline function used to initialize member variable     {    subArray.clear();    strText.Empty();    fgColor = RGB_FOREGROUND;    bgColor = RGB_BACKGROUND;    }    }LISTBOXINFO, * PLISTBOXINFO;

In order to use this LISTBOXINFO struct, the custom member functions InsertString, AddString, AddSubString help us to add context to the ListBox.

Using the code

  • InsertString: Custom member function, used to provide a public interface for external calls. This function has four parameters, insert index, text content and the foreground / background color that you set.
Collapse
/* Custom member function, Insert string and set foreground and background color for each item in ListBox. The    return value is current insert index value. */    int CMultiLineListBox::InsertString(int nIndex, LPCTSTR pszText, COLORREF fgColor, COLORREF bgColor)    {    LISTBOXINFO* pListBox = new LISTBOXINFO; // new and initialize     ASSERT(pListBox);    ASSERT((nIndex >= 0) && (nIndex <= GetCount()));    pListBox->strText = pszText;    pListBox->fgColor = fgColor;    pListBox->bgColor = bgColor;    m_sArray.insert(m_sArray.begin() + nIndex, pListBox); // insert list     return CListBox::InsertString(nIndex, pszText); // call base class InsertString function    }        
  • AddString: Custom member function, used to provide public interface for external call. This function has three parameters, text content and foreground/background color you set.
Collapse
/* Custom member function, append string and set foreground and background color for each item in ListBox. The    return value is current insert index value. */    int CMultiLineListBox::AddString(LPCTSTR pszText, COLORREF fgColor, COLORREF bgColor)    {    LISTBOXINFO* pListBox = new LISTBOXINFO; // new and initialize     ASSERT(pListBox);    pListBox->strText = pszText;    pListBox->fgColor = fgColor;    pListBox->bgColor = bgColor;    m_sArray.push_back(pListBox); // add to list     return CListBox::AddString(pszText); // call base class AddString function    }
  • AddSubString: Custom member function, used to provide a public interface for external calls. This function has four parameters, insert index, text content and the foreground / background color that you set.
Collapse
/* Custom member function, append subnode string and set foreground and background color for each item in ListBox.    The return value is current insert index value. */    void CMultiLineListBox::AddSubString(int nIndex, LPCTSTR pszText, COLORREF fgColor, COLORREF bgColor)    {    ASSERT((nIndex >=0) && (nIndex < GetCount()));    ASSERT(!m_sArray.empty());    LISTBOXINFO* pListBox = m_sArray.at(nIndex);    ASSERT(pListBox);    LISTBOXINFO::SUBNODEINFO* pSubNode = new LISTBOXINFO::SUBNODEINFO; // new and initialize     ASSERT(pSubNode);    pSubNode->strText = pszText;    pSubNode->fgColor = fgColor;    pSubNode->bgColor = bgColor;    pListBox->subArray.push_back(pSubNode); // add to subnode list    }
  • DrawItem: Override virtual function, used to draw text and set foreground / background color for each item in the ListBox.
Collapse
/* DrawItem virtual function, draw text and color for each item and subnode. */    void CMultiLineListBox::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)    {    // TODO:  Add your code to draw the specified item     ASSERT(lpDrawItemStruct->CtlType == ODT_LISTBOX);    int nIndex = lpDrawItemStruct->itemID;    if((!m_sArray.empty())  && (nIndex < static_cast(m_sArray.size())))    {    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.      CRect rc(lpDrawItemStruct->rcItem);    LISTBOXINFO* pListBox = m_sArray.at(nIndex);    ASSERT(pListBox);    if ((lpDrawItemStruct->itemAction | ODA_SELECT) &&    (lpDrawItemStruct->itemState & ODS_SELECTED))    {    dc.SetTextColor(pListBox->bgColor);    dc.SetBkColor(pListBox->fgColor);    dc.FillSolidRect(&rc, pListBox->fgColor);    // Draw item the text.       CRect rect(rc);    int nItemCount = 1;    nItemCount += static_cast(pListBox->subArray.size());    int nItemHeight = rc.Height() / nItemCount;    rect.bottom = rect.top + nItemHeight;    dc.DrawText(pListBox->strText, pListBox->strText.GetLength(), CRect(rect.left + 5, rect.top,    rect.right, rect.bottom), DT_SINGLELINE | DT_VCENTER);    // Draw subitem the text.       CRect rcItem;    rcItem.SetRectEmpty();    rcItem.top = rect.bottom;    rcItem.left = rect.left;    rcItem.right = rect.right;    rcItem.bottom = rcItem.top + nItemHeight;    vector::const_iterator iter = pListBox->subArray.begin();    for(; iter != pListBox->subArray.end(); ++iter)    {    LISTBOXINFO::SUBNODEINFO* pSubNode = *iter;    dc.SetTextColor(pSubNode->fgColor);    dc.SetBkColor(pSubNode->bgColor);    dc.FillSolidRect(&rcItem, pSubNode->bgColor);    CRect rectItem(rcItem);    rectItem.left += 22;    dc.DrawText(pSubNode->strText, pSubNode->strText.GetLength(), &rectItem,    DT_SINGLELINE | DT_VCENTER);    rcItem.top = rcItem.bottom;    rcItem.bottom = rcItem.top + nItemHeight;    }    dc.DrawFocusRect(rc); // Draw focus rect      }    else    {    dc.SetTextColor(pListBox->fgColor);    dc.SetBkColor(pListBox->bgColor);    dc.FillSolidRect(&rc, pListBox->bgColor);    // Draw the text.       CRect rect(rc);    rect.left += 5;    dc.DrawText(pListBox->strText, pListBox->strText.GetLength(), &rect, DT_SINGLELINE |    DT_VCENTER);    }    // 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 the ListBox.
Collapse
// MeasureItem virtual function, calculate text height, but the height value is fixed value in here.    void CMultiLineListBox::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct)    {    // TODO:  Add your code to determine the size of specified item     ASSERT(lpMeasureItemStruct->CtlType == ODT_LISTBOX);    lpMeasureItemStruct->itemHeight = ITEM_HEIGHT;    }
  • OnEraseBkgnd: WM_ERASEBKGND message handler function, draws background color in the ListBox.
Collapse
BOOL CMultiLineListBox::OnEraseBkgnd(CDC* pDC)    {    // Set listbox background color     CRect rc;    GetClientRect(&rc);    CDC memDC;    memDC.CreateCompatibleDC(pDC);    ASSERT(memDC.GetSafeHdc());    CBitmap bmp;    bmp.CreateCompatibleBitmap(pDC, rc.Width(), rc.Height());    ASSERT(bmp.GetSafeHandle());    CBitmap* pOldbmp = (CBitmap*)memDC.SelectObject(&bmp);    memDC.FillSolidRect(rc, LISTBOX_BACKGROUND); // Set background color which you want     pDC->BitBlt(0, 0, rc.Width(), rc.Height(), &memDC, 0, 0, SRCCOPY);    memDC.SelectObject(pOldbmp);    bmp.DeleteObject();    memDC.DeleteDC();    return TRUE;    }
  • OnKeyDown: WM_KEYDOWN message handler function, when user press direction key.
Collapse
void CMultiLineListBox::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)    {    // TODO: Add your message handler code here and/or call default     CListBox::OnKeyDown(nChar, nRepCnt, nFlags);    UpdateItem();    }
  • OnLButtonDown: WM_LBUTTONDOWN message handler function, when user click item.
Collapse
void CMultiLineListBox::OnLButtonDown(UINT nFlags, CPoint point)    {    // TODO: Add your message handler code here and/or call default     CListBox::OnLButtonDown(nFlags, point);    UpdateItem();    }
  • OnMouseMove: WM_MOUSEMOVE message handler function, when user drag mouse.
Collapse
void CMultiLineListBox::OnMouseMove(UINT nFlags, CPoint point)    {    // TODO: Add your message handler code here and/or call default     CListBox::OnMouseMove(nFlags, point);    UpdateItem();    }
  • UpdateItem: Custom member function, used to user click item, press direction key or drag mouse. The windwos message WM_LBUTTONDOWN /WM_KEYDOWN / WM_MOUSEMOVE handler function call this custom function to update item in ListBox.
Collapse
void CMultiLineListBox::UpdateItem()    {    // If per item height not equal, you must calculate area between the current focus item with last one,     // otherwise you must calculate area between the current focus item with previously focus item.     int nIndex = GetCurSel();    if((CB_ERR != nIndex) && (m_nFocusIndex != nIndex))    {    PostMessage(MSG_UPDATEITEM, (WPARAM)m_nFocusIndex, (LPARAM)nIndex);    m_nFocusIndex = nIndex; // Set current select focus index     }    }
  • OnUpdateItem: Custom message handler function, used to handler cutom message MSG_UPDATEITEM to refresh item status.
Collapse
LRESULT CMultiLineListBox::OnUpdateItem(WPARAM wParam, LPARAM lParam)    {    // MSG_UPDATEITEM message handler     int nPreIndex = static_cast(wParam);    int nCurIndex = static_cast(lParam);    if(-1 != nPreIndex)    {    SetItemHeight(nPreIndex, ITEM_HEIGHT);    }    if(-1 != nCurIndex)    {    int nItemCount = 1;    LISTBOXINFO* pListBox = m_sArray.at(nCurIndex);    ASSERT(pListBox);    nItemCount += static_cast(pListBox->subArray.size());    SetItemHeight(nCurIndex, ITEM_HEIGHT * nItemCount);    }    Invalidate(); // Update item     return 0;    }

How to Use the Control

To integrate MultiLineListBox into your own project, you first need to add the following files to your project:

  • MultiLineListBox.h
  • MultiLineListBox.cpp

Two methods to use this control class. The 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 revised CListBox to CMultiLineListBox in .h and .cpp files).

Note: This ListBox must have styles: Owner draw:variable, Selection:Single, Has strings: TRUE, Sort: FALSE.
Finally, add the following code to OnInitDialog function in dialog.cpp file.

Collapse
// OnInitDialog    ...    COLORREF clr[][2] =    {    {RGB(53, 0, 27), RGB(236, 255, 236)},    {RGB(66, 0, 33), RGB(233, 255, 233)},    {RGB(85, 0, 43), RGB(204, 255, 204)},    {RGB(106, 0, 53), RGB(191, 255, 191)},    {RGB(119, 0, 60), RGB(9, 255, 9)},    {RGB(136, 0, 68), RGB(0, 236, 0)},    {RGB(155, 0, 78), RGB(0, 225, 0)},    {RGB(168, 0, 84), RGB(0, 204, 0)},    {RGB(170, 0, 85), RGB(0, 185, 0)},    {RGB(187, 0, 94), RGB(0, 170, 0)},    {RGB(206, 0, 103), RGB(0, 151, 0)},    {RGB(211, 0, 111), RGB(0, 136, 0)},    {RGB(236, 0, 118), RGB(0, 117, 0)},    {RGB(255, 108, 182), RGB(0, 98, 0)},    {RGB(255, 121, 188), RGB(0, 89, 0)},    {RGB(255, 138, 197), RGB(0, 70, 0)},    {RGB(255, 157, 206), RGB(0, 53, 0)},    {RGB(255, 170, 212), RGB(0, 36, 0)},    {RGB(255, 193, 224), RGB(0, 21, 0)}    };    CString strText(_T(""));    int nIndex = -1;    for(int i=0; i 

The other way is dynamic create, use member function Create to generate a CMultiLineListBox object.

Note: You must set LBS_OWNERDRAWVARIABLE and LBS_HASSTRINGS styles in the Create function call.

First, you include header file MultiLineListBox.h in dialog's file. Next, create a CMultiLineListBox variable (or use Class Wizard generate a variable for CListBox object, but revised name CListBox to CMultiLineListBox in .h and .cpp files). Finally, add the following code to OnInitDialog function in dialog.cpp file.

Collapse
#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);    ...

After add this code, you can append the above code to add items and subnodes to the ListBox control.

Of course, I believe you can do better than this. Now try it yourself.
Good luck, and thank you!

License

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