头文件定义(CSWComboBox.h):
#pragma once
class CSWComboBox :
public CComboBox
{
DECLARE_DYNAMIC(CSWComboBox)
public:
CSWComboBox();
~CSWComboBox();
private:
// 初始化
void CommonConstruct();
// 销毁申请的内存空间
void ResetContent();
// 自适应列表框宽度
void AutoFitDroppedWidth();
// 获取子控件CEdit
CEdit* FindChildEdit();
// 偏移CEdit子窗体位置
void SetChildEditOffset(int nOffset = 20);
private:
struct ComboData {
DWORD_PTR dwItemData;
UINT nImageIndex;
std::string sItemText;
};
std::map m_mapItemImage;
CBrush m_brEdge; // 边框画刷
BOOL m_bIsMouseHover; // 鼠标停留
BOOL m_bIsMouseDown; // 鼠标按下
BOOL m_bShow3DEdge; // 3D边框
CSize m_szImage; // 图标尺寸
public:
void Show3DEdge(BOOL bShow = TRUE);
UINT AddImageList(Gdiplus::Bitmap* pBitmap);
protected:
void DrawEdge(COLORREF clrTopLeft, COLORREF clrBottomRight);
public:
virtual BOOL PreTranslateMessage(MSG* pMsg);
virtual void DrawItem(LPDRAWITEMSTRUCT lpDIS);
virtual void PreSubclassWindow();
virtual int AddString(LPCTSTR lpszString, int nImageIndex = -1);//加载每一项的文字和对应的图标索引
virtual int InsertString(int nIndex, LPCTSTR lpszString, int nImageIndex = -1);
virtual int GetLBText(int nIndex, CString& strText);//根据项索引获取对应的文字
virtual int GetLBImage(int nIndex, int& nImageIndex);//根据项索引获取对应的图标索引
virtual int SetItemData(int nItem, DWORD_PTR dwItemData);
virtual DWORD_PTR GetItemData(int nIndex) const;
virtual int SetCurSel(int nSelect);
virtual BOOL Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID);
protected:
afx_msg void OnPaint();
afx_msg void OnDestroy();
afx_msg void OnCbnSelchange();
afx_msg void OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct);
afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);
afx_msg void OnCbnEditchange();
afx_msg HRESULT OnResetEditPositionMsg(WPARAM, LPARAM);
DECLARE_MESSAGE_MAP()
};
源码实现(CSWComboBox.cpp):
#include "stdafx.h"
#include "CSWComboBox.h"
// CSWComboBox
CSWComboBox::CSWComboBox()
{
CommonConstruct();
}
CSWComboBox::~CSWComboBox()
{
}
IMPLEMENT_DYNAMIC(CSWComboBox, CComboBox)
BEGIN_MESSAGE_MAP(CSWComboBox, CComboBox)
//{{AFX_MSG_MAP(CSWComboBox)
ON_WM_PAINT()
ON_WM_DESTROY()
ON_CONTROL_REFLECT(CBN_SELCHANGE, &CSWComboBox::OnCbnSelchange)
ON_WM_MEASUREITEM()
ON_WM_CTLCOLOR()
ON_CONTROL_REFLECT(CBN_EDITCHANGE, &CSWComboBox::OnCbnEditchange)
ON_WM_SIZE()
ON_MESSAGE(WM_USER + 1, OnResetEditPositionMsg)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
void CSWComboBox::CommonConstruct()
{
m_brEdge.CreateSolidBrush(RGB(151, 151, 151));
m_bIsMouseHover = FALSE;
m_bIsMouseDown = FALSE;
m_bShow3DEdge = FALSE;
m_szImage = CSize(0, 0);
}
void CSWComboBox::OnPaint()
{
ModifyStyleEx (WS_EX_DLGMODALFRAME | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE, 0, SWP_FRAMECHANGED);
Default(); // 如果需要绘制下拉三角形,屏蔽此行代码
// 绘制下拉三角形
CRect rcClient;
GetClientRect(&rcClient);
// CDC* pDC = GetDC();
// CRect rcButton = rcClient;
// rcButton.left = rcButton.right - 16;
// if (rcButton.left < rcClient.left) rcButton.left = rcClient.left;
// static CPen penBlack(PS_SOLID, 1, RGB(0, 0, 0));
// static CPen penBtnShadow(PS_SOLID, 1, GetSysColor(COLOR_BTNSHADOW));
// CPen* ppenOld;
// if (IsWindowEnabled())
// ppenOld = pDC->SelectObject(&penBlack);
// else ppenOld = pDC->SelectObject(&penBtnShadow);
// for (long i = 0; i < 4; i++) {
// pDC->MoveTo(rcButton.left + 3 + i, rcButton.top + rcButton.Height() / 2 - 2 + i);
// pDC->LineTo(rcButton.left + 3 + 7 - i, rcButton.top + rcButton.Height() / 2 - 2 + i);
// if (!IsWindowEnabled())
// pDC->SetPixel(rcButton.left + 3 + 7 - i, rcButton.top + rcButton.Height() / 2 - 2 + i + 1, 0xFFFFFF);
// }
// pDC->SelectObject(ppenOld);
// ReleaseDC(pDC);
if (m_bShow3DEdge)
{
COMBOBOXINFO comboBoxInfo;
comboBoxInfo.cbSize = sizeof(COMBOBOXINFO);
GetComboBoxInfo(&comboBoxInfo);
CRect rcComboBoxBtn = comboBoxInfo.rcButton;
rcClient.right -= rcComboBoxBtn.Width();
CDC* pDC = GetDC();
rcClient.DeflateRect(1, 1, 2, 2);
if (!IsWindowEnabled())
{
pDC->Draw3dRect(rcClient, ::GetSysColor(COLOR_BTNHIGHLIGHT), ::GetSysColor(COLOR_BTNHIGHLIGHT));
rcClient.OffsetRect(1, 1);
pDC->Draw3dRect(rcClient, ::GetSysColor(COLOR_BTNHIGHLIGHT), ::GetSysColor(COLOR_BTNHIGHLIGHT));
}
else
{
pDC->Draw3dRect(rcClient, ::GetSysColor(COLOR_BTNFACE), ::GetSysColor(COLOR_BTNSHADOW));
rcClient.OffsetRect(1, 1);
pDC->Draw3dRect(rcClient, ::GetSysColor(COLOR_BTNSHADOW), ::GetSysColor(COLOR_BTNFACE));
}
ReleaseDC(pDC);
return;
}
DrawEdge(::GetSysColor(COLOR_BTNFACE), ::GetSysColor(COLOR_BTNFACE));
}
void CSWComboBox::DrawEdge(COLORREF clrTopLeft, COLORREF clrBottomRight)
{
CRect rcClient;
GetClientRect(&rcClient);
CDC* pDC = GetDC();
rcClient.DeflateRect(1, 1);
if (!IsWindowEnabled())
pDC->Draw3dRect(rcClient, ::GetSysColor(COLOR_BTNHIGHLIGHT), ::GetSysColor(COLOR_BTNHIGHLIGHT));
else
pDC->Draw3dRect(rcClient, ::GetSysColor(COLOR_BTNFACE), ::GetSysColor(COLOR_BTNFACE));
if (!IsWindowEnabled())
{
ReleaseDC(pDC);
return;
}
// 画边框颜色
GetClientRect(&rcClient);
CPen pen(PS_SOLID, 1, ::GetSysColor(COLOR_BTNSHADOW));
HGDIOBJ hOldPen = (HGDIOBJ)::SelectObject(pDC->GetSafeHdc(), (HGDIOBJ)pen.GetSafeHandle()); //选择画笔
HBRUSH hOldBrush = (HBRUSH)::SelectObject(pDC->GetSafeHdc(), (HBRUSH)::GetStockObject(HOLLOW_BRUSH)); // 选择一个空的画刷
pDC->Rectangle(rcClient.left, rcClient.top, rcClient.Width(), rcClient.Height());
::SelectObject(pDC->GetSafeHdc(), hOldBrush);
::SelectObject(pDC->GetSafeHdc(), hOldPen);
// 绘制右侧下拉按钮的外框,使其看起来平面化
{
COMBOBOXINFO comboBoxInfo;
comboBoxInfo.cbSize = sizeof(COMBOBOXINFO);
GetComboBoxInfo(&comboBoxInfo);
CRect rcComboBoxBtn = comboBoxInfo.rcButton;
CRect rcComboBoxItem = comboBoxInfo.rcItem;
CPen pen(PS_SOLID, 1, ::GetSysColor(COLOR_BTNFACE));
HGDIOBJ hOldPen = (HGDIOBJ)::SelectObject(pDC->GetSafeHdc(), (HGDIOBJ)pen.GetSafeHandle());
HBRUSH hOldBrush = (HBRUSH)::SelectObject(pDC->GetSafeHdc(), (HBRUSH)::GetStockObject(HOLLOW_BRUSH));
pDC->Rectangle(rcComboBoxItem.right - 1, rcComboBoxItem.top - 1, rcComboBoxItem.right + rcComboBoxBtn.Width() + 1, rcComboBoxItem.bottom + 1);
::SelectObject(pDC->GetSafeHdc(), hOldBrush);
::SelectObject(pDC->GetSafeHdc(), hOldPen);
}
int nIndex = GetCurSel();
if (nIndex != CB_ERR)
{
int nImageIndex;
GetLBImage(nIndex, nImageIndex);
if (nImageIndex >= 0)
{
Gdiplus::Bitmap* pBitmap = m_mapItemImage[nImageIndex];
if (pBitmap && pBitmap->GetLastStatus() == Gdiplus::Ok)
{
Gdiplus::Graphics g(pDC->GetSafeHdc());
g.DrawImage(pBitmap, RectF(6, 6, 15, 15),
0,
0,
pBitmap->GetWidth(),
pBitmap->GetHeight(),
UnitPixel,
NULL,
NULL,
NULL);
}
}
}
ReleaseDC(pDC);
}
BOOL CSWComboBox::PreTranslateMessage(MSG* pMsg)
{
if (pMsg->message == WM_KEYDOWN || pMsg->message==WM_KEYUP)
{
::TranslateMessage(pMsg);
::DispatchMessage(pMsg);
return TRUE;
}
if (pMsg->message == WM_SYSCHAR)
return TRUE;
return CComboBox::PreTranslateMessage(pMsg);
}
void CSWComboBox::DrawItem(LPDRAWITEMSTRUCT lpDIS)
{
TRACE("%s::DrawItem(%d)\n", this->GetRuntimeClass()->m_lpszClassName, GetTickCount());
CDC dc;
dc.Attach(lpDIS->hDC);
CRect rcItem = lpDIS->rcItem;
int nItem = lpDIS->itemID;
if (nItem == -1)
return;
dc.SetBkMode(TRANSPARENT);
if (lpDIS->itemState & ODS_SELECTED)
{
dc.SetTextColor(GetSysColor(COLOR_HIGHLIGHTTEXT));
dc.FillSolidRect(&rcItem, GetSysColor(COLOR_HIGHLIGHT));
if (lpDIS->itemAction & ODA_FOCUS)
dc.DrawFocusRect(&rcItem);
}
else
{
dc.SetTextColor(GetSysColor(COLOR_WINDOWTEXT));
dc.FillSolidRect(&rcItem, GetSysColor(COLOR_WINDOW));
}
// 画下拉项左侧图标
CRect rcIcon = rcItem;
rcIcon.DeflateRect(2, 2);
rcIcon.right = rcIcon.left + rcIcon.Height();
int nImageIndex;
GetLBImage(nItem, nImageIndex);
if (nImageIndex >= 0)
{
Gdiplus::Bitmap* pBitmap = m_mapItemImage[nImageIndex];
if (pBitmap && pBitmap->GetLastStatus() == Gdiplus::Ok)
{
Gdiplus::Graphics g(dc.GetSafeHdc());
g.DrawImage(pBitmap, RectF(rcIcon.left, rcIcon.top, rcIcon.Width(), rcIcon.Height()),
0,
0,
pBitmap->GetWidth(),
pBitmap->GetHeight(),
UnitPixel,
NULL,
NULL,
NULL);
rcItem.left += m_szImage.cx;
}
}
CRect rcText = rcItem;
CString strText;
GetLBText(nItem, strText);
dc.DrawText(strText, &rcText, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
if (lpDIS->itemState & ODS_SELECTED && GetDroppedState())
{
HICON hIcon = LoadIcon(NULL, IDI_ERROR);
ICONINFO icoObj;
GetIconInfo(hIcon, &icoObj);
CRect rcRIcon(rcItem);
rcRIcon.left = rcRIcon.right - icoObj.xHotspot - 2;
rcRIcon.top += (rcRIcon.Height() - icoObj.yHotspot) / 2;
DrawIconEx(dc.m_hDC, rcRIcon.left, rcRIcon.top, hIcon, icoObj.xHotspot, icoObj.yHotspot, NULL, NULL, DI_NORMAL);
}
dc.Detach();
}
void CSWComboBox::AutoFitDroppedWidth()
{
// 计算下拉列表最长宽度
CString csItem;
int nWidth = GetDroppedWidth();
CDC* pDC = this->GetDC();
for (int i = 0; i < GetCount(); i++)
{
GetLBText(i, csItem);
CSize sz = pDC->GetTextExtent(csItem);
if (sz.cx > nWidth)
{
// 如果非只读,则预留出删除叉叉的宽度
// if (m_bReadOnly)
// nWidth = sz.cx;
// else
nWidth = sz.cx + m_szImage.cx;
}
}
SetDroppedWidth(nWidth);
ReleaseDC(pDC);
}
int CSWComboBox::AddString(LPCTSTR lpszString, int nImageIndex)
{
int nItem = CComboBox::AddString(lpszString);//插入一条文本
ComboData* pData = new ComboData;
if (pData == NULL)
return -1;
pData->sItemText = lpszString;
pData->nImageIndex = nImageIndex;
CComboBox::SetItemData(nItem, (DWORD)pData);
AutoFitDroppedWidth();
return nItem;
}
int CSWComboBox::SetItemData(int nIndex, DWORD_PTR dwItemData)
{
ComboData* pData = (ComboData*)CComboBox::GetItemData(nIndex);
if (pData == NULL)
return 0;
pData->dwItemData = dwItemData;
return CComboBox::SetItemData(nIndex, (DWORD)pData);
}
DWORD_PTR CSWComboBox::GetItemData(int nIndex) const
{
ComboData* pData = (ComboData*)CComboBox::GetItemData(nIndex);
if (pData == NULL)
return 0;
return pData->dwItemData;
}
int CSWComboBox::InsertString(int nIndex, LPCTSTR lpszString, int nImageIndex)
{
int nItem = CComboBox::InsertString(nIndex, lpszString);//插入一条文本
ComboData* pData = new ComboData;
if (pData == NULL)
return -1;
pData->sItemText = lpszString;
pData->nImageIndex = nImageIndex;
CComboBox::SetItemData(nItem, (DWORD)pData);
AutoFitDroppedWidth();
return nItem;
}
int CSWComboBox::GetLBText(int nIndex, CString& strText) //获取当前绘制项的文本
{
ComboData* pData = (ComboData*)CComboBox::GetItemData(nIndex);
if (pData == NULL)
return 0;
strText = pData->sItemText.c_str();
return 1;
}
int CSWComboBox::GetLBImage(int nIndex, int& nImageIndex) //获取当前绘制项的图像
{
ComboData* pData = (ComboData*)CComboBox::GetItemData(nIndex);
if (pData == NULL)
return 0;
nImageIndex = pData->nImageIndex;
return 1;
}
void CSWComboBox::ResetContent()//释放所申请的内存
{
int nItemIndex = GetCount();
while (nItemIndex--)
{
ComboData* pData = (ComboData*)CComboBox::GetItemData(nItemIndex);
if (pData)
{
delete pData;
pData = NULL;
}
}
CComboBox::ResetContent();
}
void CSWComboBox::OnDestroy()
{
CComboBox::OnDestroy();
ResetContent();
}
void CSWComboBox::OnCbnSelchange()
{
int nSelIndex = GetCurSel();
if (nSelIndex == CB_ERR)
return;
ComboData* pData = (ComboData*)CComboBox::GetItemData(nSelIndex);
if (pData->nImageIndex!=-1)
{
CListBox* pListBox = NULL;
COMBOBOXINFO combInfo;
combInfo.cbSize = sizeof(COMBOBOXINFO);
GetComboBoxInfo(&combInfo);
if (combInfo.hwndList)
pListBox = (CListBox*)CWnd::FromHandle(combInfo.hwndList);
if (pListBox == NULL)
return;
CRect rcItem;
pListBox->GetItemRect(nSelIndex, rcItem);
CPoint point;
GetCursorPos(&point);
ScreenToClient(&point);
CEdit *pEdit = FindChildEdit();
if (pEdit)
{
CRect rcEdit;
pEdit->GetClientRect(&rcEdit);
point.y -= rcEdit.Height();
}
rcItem.left = rcItem.right - 18;
if (rcItem.PtInRect(point))
{
if (pData)
{
delete pData;
pData = NULL;
}
this->DeleteString(nSelIndex);
//pListBox->DeleteString(nSelIndex);
SetWindowText(_T(""));
}
else
{
SetChildEditOffset(20);
}
Invalidate();
}
}
void CSWComboBox::Show3DEdge(BOOL bShow)
{
m_bShow3DEdge = bShow;
RedrawWindow();
}
UINT CSWComboBox::AddImageList(Gdiplus::Bitmap* pBitmap)
{
if (pBitmap == NULL)
return UINT(-1);
UINT nCount = m_mapItemImage.size();
m_mapItemImage[nCount] = pBitmap;
m_szImage = CSize(max(m_szImage.cx, pBitmap->GetWidth()), max(m_szImage.cy, pBitmap->GetHeight()));
return nCount;
}
void CSWComboBox::PreSubclassWindow()
{
// TODO: 在此添加专用代码和/或调用基类
SetWindowLong(m_hWnd, GWL_EXSTYLE, GetWindowLong(m_hWnd, GWL_STYLE) | BS_OWNERDRAW);
CComboBox::PreSubclassWindow();
}
void CSWComboBox::OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
CComboBox::OnMeasureItem(nIDCtl, lpMeasureItemStruct);
}
HBRUSH CSWComboBox::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
HBRUSH hbr = CComboBox::OnCtlColor(pDC, pWnd, nCtlColor);
// TODO: 在此更改 DC 的任何特性
if (nCtlColor == CTLCOLOR_STATIC)
{
// CFont* font = new CFont;
// font->CreatePointFont(72, _T("宋体"));
// pDC->SelectObject(font);
pDC->SetTextColor(GetSysColor(COLOR_WINDOWTEXT));
pDC->SetBkMode(TRANSPARENT);
HBRUSH hBrush = CreateSolidBrush(GetSysColor(COLOR_WINDOW));
// font->DeleteObject();
// delete font;
return (HBRUSH)hBrush;
}
if (nCtlColor == CTLCOLOR_EDIT)
{
CEdit* pEdit = reinterpret_cast(pWnd);
//pEdit->SetLimitText(256);
pDC->SetTextColor(GetSysColor(COLOR_WINDOWTEXT));
// CFont* font = new CFont;
// font->CreatePointFont(72, _T("宋体"));
// pDC->SelectObject(font);
pDC->SetBkMode(TRANSPARENT);
HBRUSH hBrush = CreateSolidBrush(GetSysColor(COLOR_WINDOW));
// font->DeleteObject();
// delete font;
return (HBRUSH)hBrush;
}
if (nCtlColor == CTLCOLOR_LISTBOX)
{
// CFont* font = new CFont;
// font->CreatePointFont(72, _T("宋体"));
// pDC->SelectObject(font);
pDC->SetTextColor(GetSysColor(COLOR_WINDOWTEXT));
pDC->SetBkMode(TRANSPARENT);
HBRUSH hBrush = CreateSolidBrush(GetSysColor(COLOR_WINDOW));
// font->DeleteObject();
// delete font;
return (HBRUSH)hBrush;
}
// TODO: 如果默认的不是所需画笔,则返回另一个画笔
return hbr;
}
void CSWComboBox::OnCbnEditchange()
{
// TODO: 在此添加控件通知处理程序代码
SetChildEditOffset(0);
}
int CSWComboBox::SetCurSel(int nSelect)
{
int nIndex = CComboBox::SetCurSel(nSelect);
if (nIndex != CB_ERR)
PostMessage(WM_USER+1);
return nIndex;
}
// 获取子控件CEdit
CEdit* CSWComboBox::FindChildEdit()
{
::CWnd *pWnd = GetWindow(GW_CHILD);
while (pWnd)
{
TCHAR classname[256];
::GetClassName(pWnd->m_hWnd, classname, 256);
if (lstrcmpi(classname, _T("Edit")) == 0)
break;
if (pWnd)
pWnd = pWnd->GetNextWindow();
}
return (CEdit*)pWnd;
}
// 偏移CEdit子窗体位置
void CSWComboBox::SetChildEditOffset(int nOffset/* = 20*/)
{
CEdit *pEdit = FindChildEdit();
if (pEdit)
{
CRect rcEditWindow; pEdit->GetWindowRect(rcEditWindow);
CRect rcWindow; GetWindowRect(rcWindow);
rcEditWindow.left = nOffset + 3;
rcEditWindow.top = 6; // 相对位置本来是3,考虑到文本居中显示,6比较合适
rcEditWindow.right = rcWindow.Width() - 20;
rcEditWindow.bottom = rcWindow.Height() - 3;
pEdit->MoveWindow(rcEditWindow);
}
}
HRESULT CSWComboBox::OnResetEditPositionMsg(WPARAM wParam, LPARAM lParam)
{
SetChildEditOffset();
return S_OK;
}
BOOL CSWComboBox::Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID)
{
// TODO: 在此添加专用代码和/或调用基类
dwStyle &= ~0x0F;
dwStyle |= CBS_DROPDOWNLIST;
dwStyle |= CBS_OWNERDRAWFIXED;
dwStyle |= CBS_HASSTRINGS;
return CComboBox::Create(dwStyle, rect, pParentWnd, nID);
}
效果图如下: