MFC 自繪ComboBox

效果圖:html


.h文件函數

#pragma once
#include"stdafx.h"

class CComboBoxXI : public CComboBox
{
	DECLARE_DYNAMIC(CComboBoxXI)

public:
	CComboBoxXI();
	virtual ~CComboBoxXI();

	void SetImageList(CImageList* pImageList);
	CImageList* GetImageList() const;
	int GetItemImage(int nIndex);
	int SetItemImage(int nIndex, int nImageIndex, BOOL bRepaint = TRUE);

	int AddString(LPCTSTR lpszString, int nImageIndex = -1);
	int DeleteString(UINT nIndex);
	int InsertString(int nIndex, LPCTSTR lpszString, int nImageIndex);
	void ResetContent();

	DWORD_PTR GetItemData(int nIndex) const;
	int SetItemData(int nIndex, DWORD_PTR dwItemData);
	void* GetItemDataPtr(int nIndex) const;
	int SetItemDataPtr(int nIndex, void* pData);

protected:
	virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
	virtual void MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct);

	DECLARE_MESSAGE_MAP()

private:
	// 關聯的CImageList
	CImageList *m_pImageList;
	CSize      m_imageSize;

	// EditCtrl的高度
	unsigned int  m_nEditHeight;

	// 是不是DropList:
	// -1(待定,未檢測), 1(Yes), 0(No)
	// 只對DropList設置EditCtrl的高度,對DropDown和Simple則採用默認高度
	int m_iIsDropList;

	struct CBDataXI
	{
		// 圖像序號,若是爲-1,則表示無圖像
		int iImageIndex;

		// 關聯數據的指針
		LPVOID pData;

		CBDataXI()
		{
			iImageIndex = -1;
			pData = NULL;
		}
	};

	void DeleteItemData(int nIndex);
};

.cpp 文件

/**********************************************************
 ComboBoxXI.cpp : implementation file
 Written by WangYao (wangyao1052@163.com)
 Version: V1.0 2014-06-07
**********************************************************/

#include "stdafx.h"
#include "ComboBoxXI.h"

#define	CBXI_CX_BORDER   2
#define	CBXI_CY_BORDER   2

// CComboBoxXI

IMPLEMENT_DYNAMIC(CComboBoxXI, CComboBox)

CComboBoxXI::CComboBoxXI()
{
	m_pImageList = NULL;
	m_imageSize.cx = 0;
	m_imageSize.cy = 0;
	m_nEditHeight = 0;
	m_iIsDropList = -1;
}

CComboBoxXI::~CComboBoxXI()
{
}

//---------------------------------------------------------
// 函數: SetImageList
// 功能: 設置關聯的ImageList
// 參數: 
//       pImageList --- ImageList的指針
// 返回: 無
//---------------------------------------------------------
void CComboBoxXI::SetImageList(CImageList* pImageList)
{
	m_pImageList = pImageList;
	if (m_pImageList)
	{
		int cx;
		int cy;
		ImageList_GetIconSize(*m_pImageList, &cx, &cy);
		m_imageSize.cx = cx;
		m_imageSize.cy = cy;
	}
	else
	{
		m_imageSize.cx = 0;
		m_imageSize.cy = 0;
	}

	Invalidate();
}

//---------------------------------------------------------
// 函數: GetImageList
// 功能: 獲取關聯ImageList的指針
// 參數: 無
// 返回: 關聯ImageList的指針,若沒有關聯ImageList返回NULL
//---------------------------------------------------------
CImageList* CComboBoxXI::GetImageList() const
{
	return m_pImageList;
}

//---------------------------------------------------------
// 函數: GetItemImage
// 功能: 獲取項的圖片索引
// 參數: 
//       nIndex  --- 項索引(以0爲起點)
// 返回:
//       以0爲起始的圖片序號
//       若存在錯誤,如nIndex超出範圍,返回-1
//       若組合框項沒有關聯圖片則返回-1
//---------------------------------------------------------
int CComboBoxXI::GetItemImage(int nIndex)
{
	CBDataXI* pCBData = (CBDataXI*)CComboBox::GetItemData(nIndex);
	if (pCBData == (CBDataXI*)CB_ERR || pCBData == NULL)
	{
		return -1;
	}

	return pCBData->iImageIndex;
}

//---------------------------------------------------------
// 函數: SetItemImage
// 功能: 設置項的圖片索引
// 參數: 
//       nIndex      --- 項索引(以0爲起點)
//       nImageIndex --- 圖片索引(以0爲起點)
//       bRepaint    --- 是否重繪
// 返回:
//       成功返回0,失敗返回-1
//---------------------------------------------------------
int CComboBoxXI::SetItemImage(int nIndex, int nImageIndex, BOOL bRepaint)
{
	CBDataXI* pCBData = (CBDataXI*)CComboBox::GetItemData(nIndex);
	if (pCBData == (CBDataXI*)CB_ERR || pCBData == NULL)
	{
		return -1;
	}
	pCBData->iImageIndex = nImageIndex;

	if (bRepaint)
	{
		Invalidate();
	}

	return 0;
}

//---------------------------------------------------------
// 函數: AddString
// 功能: 添加項
// 參數: lpszString  --- 字符串指針
//        nImageIndex --- 圖片索引(以0爲起點)
// 返回: 新增項的索引值
//        失敗則返回CB_ERR或CB_ERRSPACE
//---------------------------------------------------------
int CComboBoxXI::AddString(LPCTSTR lpszString, int nImageIndex)
{
	int nIndex = CComboBox::AddString(lpszString);
	if (nIndex != CB_ERR && nIndex != CB_ERRSPACE)
	{
		CBDataXI *pData = new CBDataXI();
		pData->iImageIndex = nImageIndex;
		pData->pData = NULL;
		CComboBox::SetItemData(nIndex, (DWORD_PTR)pData);
	}
	
	return nIndex;
}

//---------------------------------------------------------
// 函數: InsertString
// 功能: 在指定位置插入項
// 參數: 
//       nIndex      --- 指定位置
//       lpszString  --- 字符串指針
//       nImageIndex --- 圖片索引(以0爲起點)
// 返回: 
//       插入項索引值
//       失敗則返回CB_ERR或CB_ERRSPACE
//---------------------------------------------------------
int CComboBoxXI::InsertString(int nIndex, LPCTSTR lpszString, int nImageIndex)
{
	int nRet = CComboBox::InsertString(nIndex, lpszString);
	if (nRet != CB_ERR && nRet != CB_ERRSPACE)
	{
		CBDataXI *pData = new CBDataXI();
		pData->iImageIndex = nImageIndex;
		pData->pData = NULL;
		CComboBox::SetItemData(nIndex, (DWORD_PTR)pData);
	}

	return nRet;
}

//---------------------------------------------------------
// 函數: DeleteString
// 功能: 刪除項
// 參數: 
//       nIndex  --- 索引
// 返回: 
//       成功則返回當前還剩的項數
//       失敗則返回CB_ERR
//---------------------------------------------------------
int CComboBoxXI::DeleteString(UINT nIndex)
{
	DeleteItemData(nIndex);

	return CComboBox::DeleteString(nIndex);
}

//---------------------------------------------------------
// 函數: ResetContent
// 功能: 清空內容
//---------------------------------------------------------
void CComboBoxXI::ResetContent()
{
	int nCnt = CComboBox::GetCount();
	for (int i = 0; i < nCnt; ++i)
	{
		DeleteItemData(i);
	}

	CComboBox::ResetContent();
}

//---------------------------------------------------------
// 函數: GetItemData
// 功能: 獲取項關聯的數據
// 參數:
//       nIndex --- 索引
// 返回:
//       關聯的數據,失敗則返回CB_ERR
//---------------------------------------------------------
DWORD_PTR CComboBoxXI::GetItemData(int nIndex) const
{
	CBDataXI* pCBData = (CBDataXI*)CComboBox::GetItemData(nIndex);
	if (pCBData == (CBDataXI*)CB_ERR || pCBData == NULL)
	{
		return CB_ERR;
	}

	return (DWORD_PTR)pCBData->pData;
}

//---------------------------------------------------------
// 函數: SetItemData
// 功能: 設置項關聯的數據
// 參數:
//       nIndex     --- 索引
//       dwItemData --- 關聯的數據 
// 返回:
//       成功返回CB_OKAY, 失敗返回CB_ERR
//---------------------------------------------------------
int CComboBoxXI::SetItemData(int nIndex, DWORD_PTR dwItemData)
{
	CBDataXI* pCBData = (CBDataXI*)CComboBox::GetItemData(nIndex);
	if (pCBData == (CBDataXI*)CB_ERR || pCBData == NULL)
	{
		return CB_ERR;
	}

	pCBData->pData = (LPVOID)dwItemData;
	return CB_OKAY;
}

//---------------------------------------------------------
// 函數: GetItemDataPtr
// 功能: 獲取項關聯的數據
// 參數:
//       nIndex --- 索引
// 返回:
//       關聯的數據,失敗則返回CB_ERR
//---------------------------------------------------------
void* CComboBoxXI::GetItemDataPtr(int nIndex) const
{
	CBDataXI* pCBData = (CBDataXI*)CComboBox::GetItemDataPtr(nIndex);
	if (pCBData == (CBDataXI*)CB_ERR || pCBData == NULL)
	{
		return (void*)CB_ERR;
	}

	return pCBData->pData;
}

//---------------------------------------------------------
// 函數: SetItemDataPtr
// 功能: 設置項關聯的數據
// 參數:
//       nIndex --- 索引
//       pData  --- 關聯的數據
// 返回:
//       成功返回CB_OKAY, 失敗則返回CB_ERR
//---------------------------------------------------------
int CComboBoxXI::SetItemDataPtr(int nIndex, void* pData)
{
	CBDataXI* pCBData = (CBDataXI*)CComboBox::GetItemDataPtr(nIndex);
	if (pCBData == (CBDataXI*)CB_ERR || pCBData == NULL)
	{
		return CB_ERR;
	}

	pCBData->pData = pData;
	return CB_OKAY;
}

void CComboBoxXI::DeleteItemData(int nIndex)
{
	CBDataXI* pCBData = (CBDataXI*)CComboBox::GetItemData(nIndex);
	if (pCBData != (CBDataXI*)CB_ERR)
	{
		delete pCBData;
		CComboBox::SetItemData(nIndex, (DWORD_PTR)NULL);
	}
}

void CComboBoxXI::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
	if (lpDrawItemStruct->itemID == -1)
	{
		return;
	}

	CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
	pDC->SetBkMode(TRANSPARENT);
	RECT& rcItem = lpDrawItemStruct->rcItem;

	CString cstrItemText;
	CComboBox::GetLBText(lpDrawItemStruct->itemID, cstrItemText);
	
	CBDataXI* pData = (CBDataXI*)CComboBox::GetItemDataPtr(lpDrawItemStruct->itemID);
	if (NULL == pData)
	{
		return;
	}

	// 是否處於選中狀態
	BOOL bIsSelected = (lpDrawItemStruct->itemAction | ODA_SELECT) 
		&& (lpDrawItemStruct->itemState & ODS_SELECTED);

	// 是否處於焦點狀態
	BOOL bIsFocused = (lpDrawItemStruct->itemAction | ODA_FOCUS) 
		&& (lpDrawItemStruct->itemState & ODS_FOCUS);

	// 繪製背景
	if (bIsSelected)
	{
		COLORREF oldColor = pDC->GetBkColor();
		pDC->FillSolidRect(&rcItem, ::GetSysColor(COLOR_HIGHLIGHT));
		pDC->SetBkColor(oldColor);
	}
	else
	{
		pDC->FillSolidRect(&rcItem, pDC->GetBkColor());
	}

	// 繪製ICON
	CRect rcIcon(rcItem.left, rcItem.top, rcItem.left, rcItem.top);
	if (m_pImageList)
	{
		HICON hIcon = m_pImageList->ExtractIcon(pData->iImageIndex);
		if (hIcon)
		{
			rcIcon.right = rcItem.left + m_imageSize.cx + 2 * CBXI_CX_BORDER;
			rcIcon.bottom = rcItem.top + m_imageSize.cy + 2 * CBXI_CY_BORDER;

			CPoint pos(rcIcon.left + CBXI_CX_BORDER, rcIcon.top + CBXI_CY_BORDER);
			pDC->DrawState(pos, m_imageSize, hIcon, DSS_NORMAL, (CBrush*)NULL);
			::DestroyIcon(hIcon);
		}
	}

	// 繪製Text
	if (bIsSelected)
	{
		pDC->SetTextColor(::GetSysColor(COLOR_HIGHLIGHTTEXT));
	}
	else
	{
		pDC->SetTextColor(::GetSysColor(COLOR_WINDOWTEXT));
	}
	CRect rcText(rcItem);
	rcText.left = rcIcon.right;
	rcText.top = rcIcon.top;
	pDC->DrawText(cstrItemText, rcText, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_WORDBREAK);
}

void CComboBoxXI::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
	CDC *pDC = GetDC();
	LPCTSTR lpszText = (LPCTSTR)lpMeasureItemStruct->itemData;
	CSize szText = pDC->GetTextExtent(lpszText);
	ReleaseDC(pDC);

	lpMeasureItemStruct->itemHeight = 
		szText.cy > m_imageSize.cy + 2 * CBXI_CY_BORDER ? szText.cy :  m_imageSize.cy + 2 * CBXI_CY_BORDER;

	// 檢測是不是DropList
	if (-1 == m_iIsDropList)
	{
		DWORD dwStyle = GetStyle();
		if (DWORD(CBS_DROPDOWNLIST & dwStyle) == (DWORD)CBS_DROPDOWNLIST)
		{
			m_iIsDropList = 1;
		}
		else
		{
			m_iIsDropList = 0;
		}
	}

	// 設置EditCtrl的高度
	if (m_iIsDropList == 1 && lpMeasureItemStruct->itemHeight > m_nEditHeight)
	{
		m_nEditHeight = lpMeasureItemStruct->itemHeight;
		SetItemHeight(-1, m_nEditHeight);
	}
}

BEGIN_MESSAGE_MAP(CComboBoxXI, CComboBox)
END_MESSAGE_MAP()

// CComboBoxXI message handlers

說明:

=============== 提供了以下接口 ===============================
void SetImageList(CImageList* pImageList);
CImageList* GetImageList() const;
int GetItemImage(int nIndex);
int SetItemImage(int nIndex, int nImageIndex, BOOL bRepaint = TRUE);


int AddString(LPCTSTR lpszString, int nImageIndex = -1);
int DeleteString(UINT nIndex);
int InsertString(int nIndex, LPCTSTR lpszString, int nImageIndex);
void ResetContent();


DWORD_PTR GetItemData(int nIndex) const;
int SetItemData(int nIndex, DWORD_PTR dwItemData);
void* GetItemDataPtr(int nIndex) const;
int SetItemDataPtr(int nIndex, void* pData);




================= 示例 =================================
OnInitialDialog()
{
...........

m_imglist1.Create(32,32, ILC_COLOR32 | ILC_MASK, 2, 1);
m_imglist1.Add(AfxGetApp()->LoadIcon(IDI_ICON1));
m_imglist1.Add(AfxGetApp()->LoadIcon(IDI_ICON2));


m_combo.SetImageList(&m_imglist1);
m_combo.AddString(_T("123"), 0);
m_combo.AddString(_T("456"), 1);
m_combo.SetCurSel(0);
}


注:ComboBox要設置兩個屬性 
HasStrings: True 
Owner Draw: Variable
spa