一个简单的扩展的CListBox类,点击某一个item的时候,自动展开该项来显示更多信息,类似CTreeCtrl控件的Expand样式风格。
#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()
};
// 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;
}
// 主对话框的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]);
}
}
}
运行效果图如下所示: