一个简单的扩展的CListBox类,点击某一个item的时候,自动展开该项来显示更多信息,类似CTreeCtrl控件的Expand样式风格。
// H 文件 #pragma once #include <vector> using namespace std; // CMultiLineListBox #define RGB_FOREGROUND RGB(0, 0, 0) #define RGB_BACKGROUND RGB(255, 255, 255) #define LISTBOX_BACKGROUND RGB(236, 255, 236) class CMultiLineListBox : public CListBox { DECLARE_DYNAMIC(CMultiLineListBox) public: CMultiLineListBox(); virtual ~CMultiLineListBox(); typedef struct _LISTBOX_INFO_ { public: typedef struct _SUBNODE_INFO_ { public: CString strText; COLORREF fgColor; COLORREF bgColor; _SUBNODE_INFO_() { clean(); } ~_SUBNODE_INFO_() { clean(); } protected: inline void clean(void) { strText.Empty(); fgColor = RGB_FOREGROUND; bgColor = RGB_BACKGROUND; } }SUBNODEINFO, *PSUBNODEINFO; public: vector<SUBNODEINFO*> subArray; CString strText; COLORREF fgColor; COLORREF bgColor; _LISTBOX_INFO_() { clean(); } ~_LISTBOX_INFO_() { clean(); } protected: inline void clean(void) { subArray.clear(); strText.Empty(); fgColor = RGB_FOREGROUND; bgColor = RGB_BACKGROUND; } }LISTBOXINFO, * PLISTBOXINFO; protected: static int m_nFocusIndex; vector<LISTBOXINFO*> m_sArray; public: int InsertString(int nIndex, LPCTSTR pszText, COLORREF fgColor = RGB_FOREGROUND, COLORREF bgColor = RGB_BACKGROUND); int AddString(LPCTSTR pszText, COLORREF fgColor = RGB_FOREGROUND, COLORREF bgColor = RGB_BACKGROUND); void AddSubString(int nIndex, LPCTSTR pszText, COLORREF fgColor = RGB_FOREGROUND, COLORREF bgColor = RGB_BACKGROUND); protected: virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct); virtual void MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct); void UpdateItem(void); void GetItemHeight(int nIndex); protected: afx_msg BOOL OnEraseBkgnd(CDC* pDC); afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags); afx_msg void OnLButtonDown(UINT nFlags, CPoint point); afx_msg void OnMouseMove(UINT nFlags, CPoint point); afx_msg LRESULT OnUpdateItem(WPARAM wParam, LPARAM lParam); DECLARE_MESSAGE_MAP() };
// CPP 文件 // MultiLineListBox.cpp : implementation file // #include "stdafx.h" #include "MultiLineListBox.h" // CMultiLineListBox #define MSG_UPDATEITEM WM_USER + 0x1001 #define ITEM_HEIGHT 20 int CMultiLineListBox::m_nFocusIndex = -1; IMPLEMENT_DYNAMIC(CMultiLineListBox, CListBox) CMultiLineListBox::CMultiLineListBox() { m_sArray.clear(); } CMultiLineListBox::~CMultiLineListBox() { vector<LISTBOXINFO*>::const_iterator iter1 = m_sArray.begin(); for(; iter1 != m_sArray.end(); ++iter1) { LISTBOXINFO* pNode = *iter1; vector<LISTBOXINFO::SUBNODEINFO*>::const_iterator iter2 = pNode->subArray.begin(); for(; iter2 != pNode->subArray.end(); ++iter2) { LISTBOXINFO::SUBNODEINFO* pSubNode = *iter2; delete pSubNode; pSubNode = NULL; } delete pNode; pNode = NULL; } m_sArray.clear(); } BEGIN_MESSAGE_MAP(CMultiLineListBox, CListBox) ON_WM_ERASEBKGND() ON_WM_KEYDOWN() ON_WM_LBUTTONDOWN() ON_WM_MOUSEMOVE() ON_MESSAGE(MSG_UPDATEITEM, &CMultiLineListBox::OnUpdateItem) END_MESSAGE_MAP() int CMultiLineListBox::InsertString(int nIndex, LPCTSTR pszText, COLORREF fgColor, COLORREF bgColor) { LISTBOXINFO* pListBox = new LISTBOXINFO; ASSERT(pListBox); ASSERT((nIndex >= 0) && (nIndex <= GetCount())); pListBox->strText = pszText; pListBox->fgColor = fgColor; pListBox->bgColor = bgColor; m_sArray.insert(m_sArray.begin() + nIndex, pListBox); return CListBox::InsertString(nIndex, pszText); } int CMultiLineListBox::AddString(LPCTSTR pszText, COLORREF fgColor, COLORREF bgColor) { LISTBOXINFO* pListBox = new LISTBOXINFO; ASSERT(pListBox); pListBox->strText = pszText; pListBox->fgColor = fgColor; pListBox->bgColor = bgColor; m_sArray.push_back(pListBox); return CListBox::AddString(pszText); } 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; ASSERT(pSubNode); pSubNode->strText = pszText; pSubNode->fgColor = fgColor; pSubNode->bgColor = bgColor; pListBox->subArray.push_back(pSubNode); } // CMultiLineListBox message handlers 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; } 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(); } } 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; } 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(); } void CMultiLineListBox::OnLButtonDown(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default CListBox::OnLButtonDown(nFlags, point); UpdateItem(); } void CMultiLineListBox::OnMouseMove(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default CListBox::OnMouseMove(nFlags, point); UpdateItem(); } 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 } } LRESULT CMultiLineListBox::OnUpdateItem(WPARAM wParam, LPARAM lParam) { 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; }
// 调用方法 // 成员变量,关联了CListBox窗口对象 CMultiLineListBox m_listBox; // 资源编辑CListBox属性设置Owner Draw: Variable, Selection: Single, Has Strings: TRUE // 主对话框的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++) { 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++) { 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]); } } }
运行效果图如下所示: