• 所有者绘制的CListBox 版本2


    介绍 这与这里的链接的上一篇文章有点类似。区别在于,这个CMultiLineListBox类支持动态多行显示。当用户单击或选择列表框中的一个项目时,该项目将展开以显示更多信息。它看起来像一个CTreeCtrl控件。 实现 CMultiLineListBox派生自CListBox。重要提示:您必须重写DrawItem和测量项目虚函数。这两个功能完成了主绘图操作。此外,它处理窗口消息像WM_ERASEBKGND, WM_KEYDOWN, WM_LBUTTONDOWN, WM_MOUSEMOVE和自定义消息MSG_UPDATEITEM。当用户单击一个项目、拖动鼠标或按方向键(向上/向下键)时,将发布MSG_UPDATEITEM消息。 在这个类中,我们定义了一个名为LISTBOX_INFO的重要结构。这个结构体在列表框中存储每个项目的信息。结构定义是这样的:

    // 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<SUBNODEINFO*> 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;

    为了使用这个LISTBOXINFO结构体,自定义成员函数InsertString, AddString, AddSubString帮助我们添加上下文到ListBox。 使用的代码 自定义成员函数,用于为外部调用提供公共接口。这个函数有四个参数:插入索引、文本内容和您设置的前景色/背景色。

    /* 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
    }        

    自定义成员函数,用于为外部调用提供公共接口。这个函数有三个参数,文本内容和你设置的前/背景颜色。

    /* 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
    }

    自定义成员函数,用于为外部调用提供公共接口。这个函数有四个参数:插入索引、文本内容和您设置的前景色/背景色。

    /* 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 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<int>(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<int>(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<LISTBOXINFO::SUBNODEINFO*>::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:覆盖虚拟函数,用于计算列表框中每个项的当前文本高度。

    // 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;
    }

    消息处理程序函数,在列表框中绘制背景颜色。

    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;
    }

    WM_KEYDOWN消息处理函数,当用户按方向键时。

    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();
    }

    当用户点击项目时,WM_LBUTTONDOWN消息处理函数。

    void CMultiLineListBox::OnLButtonDown(UINT nFlags, CPoint point)
    {
     // TODO: Add your message handler code here and/or call default
     CListBox::OnLButtonDown(nFlags, point);
     UpdateItem();
    }

    当用户拖动鼠标时,WM_MOUSEMOVE消息处理函数。

    void CMultiLineListBox::OnMouseMove(UINT nFlags, CPoint point)
    {
     // TODO: Add your message handler code here and/or call default
     CListBox::OnMouseMove(nFlags, point);
     UpdateItem();
    }

    自定义成员功能,用于用户点击项目,按方向键或拖动鼠标。windwos消息WM_LBUTTONDOWN /WM_KEYDOWN / WM_MOUSEMOVE处理函数调用这个自定义函数来更新列表框中的项。

    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:自定义消息处理程序函数,用于处理cutom消息MSG_UPDATEITEM来刷新项目状态。

    LRESULT CMultiLineListBox::OnUpdateItem(WPARAM wParam, LPARAM lParam)
    {
     // MSG_UPDATEITEM message handler
     int nPreIndex = static_cast<int>(wParam);
     int nCurIndex = static_cast<int>(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<int>(pListBox->subArray.size());
      SetItemHeight(nCurIndex, ITEM_HEIGHT * nItemCount);
     }
      Invalidate(); // Update item
     return 0;
    }

    如何使用控件 要将MultiLineListBox集成到您自己的项目中,您首先需要添加以下文件到您的项目: MultiLineListBox.h MultiLineListBox.cpp 使用此控件类的两个方法。一个是静态关联,另一个是动态创建。首先,您还需要添加ListBox控件到对话框模板。接下来,在对话框的h文件中包含头文件MultiLineListBox.h,并创建一个CMultiLineListBox变量(或者使用类向导为CListBox对象生成一个变量,但是在.h和.cpp文件中将CListBox修改为CMultiLineListBox)。 注意:这个列表框必须有样式:所有者绘制:变量,选择:单,字符串:真,排序:假。最后,将以下代码添加到dialog.cpp文件中的OnInitDialog函数中。

    // 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<sizeof(clr)/sizeof(clr[0]); i++) // Add item in ListBox
     {
      strText.Format(_T("%02d - Hello, World!"), i+1);
      nIndex = m_listBox.AddString(strText, clr[i][0], clr[i][1]);
      if(i % 2)
      {
       for(int j=0; j<3; j++) // Add subnode to item in ListBox
       {
        strText.Format(_T("%02d.%02d - Hello, World!"), i+1, j+1);
        m_listBox.AddSubString(nIndex, strText, clr[i][1], clr[i][0]);
       }
      }
      else
      {
       for(int j=0; j<2; j++)
       {
        strText.Format(_T("%02d.%02d - Hello, World!"), i+1, j+1);
        m_listBox.AddSubString(nIndex, strText, clr[i][1], clr[i][0]);
       }
      }
     }
    ...

    另一种方法是动态创建,使用成员函数create来生成CMultiLineListBox对象。 注意:您必须在创建函数调用中设置LBS_OWNERDRAWVARIABLE和LBS_HASSTRINGS样式。 首先,在对话框的文件中包含头文件MultiLineListBox.h。接下来,创建一个CMultiLineListBox变量(或者使用类向导为CListBox对象生成一个变量,但是在.h和.cpp文件中将名称CListBox修改为CMultiLineListBox)。最后,将以下代码添加到dialog.cpp文件中的OnInitDialog函数中。

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

    添加此代码后,可以添加上述代码,以向ListBox控件添加项和子节点。 当然,我相信你能做得更好。现在你自己试试。祝你好运,谢谢! 本文转载于:http://www.diyabc.com/frontweb/news242.html

  • 相关阅读:
    border——边框属性
    CSS1,CSS2选择器详解
    CSS样式表与HTML结合的方法
    详细解析HTML基础结构
    jquery 解析xml
    asp.net js调用后台方法
    asp.net利用剪切板导出excel
    webform 不实用office控件导出excel StringBuilder 类型拼接字符串表格导出excel
    Android再学习-20141018-布局-进度条
    Android再学习-20140928-布局
  • 原文地址:https://www.cnblogs.com/Dincat/p/13431434.html
Copyright © 2020-2023  润新知