VC++ CComboBox自绘(选择下拉列表框)

VC++(基础篇) 同时被 3 个专栏收录
61 篇文章 0 订阅
58 篇文章 0 订阅

效果图:

头文件定义(CSWCheckComboBox.h):

#pragma once

class CSWCheckComboBox : public CComboBox
{
	DECLARE_DYNAMIC(CSWCheckComboBox)
	// 成员私有结构定义

	// 构造/析构函数
public:
	CSWCheckComboBox();
	virtual ~CSWCheckComboBox();

	// 私有成员变量
private:

	// 私有成员函数
private:
	void CommonConstruct(); // 初始化

	// 受保护成员变量
protected:
	CListBox* m_pListBox;
	WNDPROC m_pOldWndProc;

	// 受保护成员函数
protected:
	CString UpdateCheckedText(BOOL bReupdate = TRUE);
	LRESULT OnListBoxWindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);

	// 虚函数
protected:
	virtual BOOL Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID);
	virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);

	// 消息函数
protected:
	DECLARE_MESSAGE_MAP()
	afx_msg LRESULT OnCtlColorListBox(WPARAM wParam, LPARAM lParam);
	afx_msg LRESULT OnGetText(WPARAM wParam, LPARAM lParam);
	afx_msg LRESULT OnGetTextLength(WPARAM wParam, LPARAM lParam);

	// 共有成员变量
public:
	int SetCheck(int nIndex, int nCheck, BOOL bRepaint = TRUE);
	BOOL GetCheck(int nIndex);
	void CheckAll(BOOL bCheck = TRUE);
	
	// 共有成员函数
public:

	// 静态成员变量
public:

	// 静态成员函数
public:	
	static LRESULT ListBoxWindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
};

源码实现(CSWCheckComboBox.cpp):

#include "stdafx.h"
#include "CSWCheckComboBox.h"

CSWCheckComboBox::CSWCheckComboBox()
{
	CommonConstruct();
}

CSWCheckComboBox::~CSWCheckComboBox()
{
}

IMPLEMENT_DYNAMIC(CSWCheckComboBox, CComboBox)
BEGIN_MESSAGE_MAP(CSWCheckComboBox, CComboBox)
	//{{AFX_MSG_MAP(CSWCheckComboBox)
	ON_MESSAGE(WM_CTLCOLORLISTBOX, OnCtlColorListBox)
	ON_MESSAGE(WM_GETTEXT, OnGetText)
	ON_MESSAGE(WM_GETTEXTLENGTH, OnGetTextLength)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

void CSWCheckComboBox::CommonConstruct()
{
	m_pListBox = NULL;
	m_pOldWndProc = NULL;
}

LRESULT CSWCheckComboBox::ListBoxWindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	CSWCheckComboBox* pThis = (CSWCheckComboBox*)GetProp(hWnd, _T("CComboBoxHelper"));
	if (pThis == NULL)
		return S_OK;

	return pThis->OnListBoxWindowProc(hWnd, message, wParam, lParam);
}

LRESULT CSWCheckComboBox::OnListBoxWindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	TRACE(_T("%d...%d\n"), message, GetTickCount());
	switch (message)
	{
	case LB_GETCURSEL:
		return -1;
	case WM_KEYDOWN:
	{
		if (wParam == VK_DOWN || wParam == VK_UP)
		{
			int nIndex = CallWindowProc(m_pOldWndProc, hWnd, LB_GETCURSEL, wParam, lParam);
			if (wParam == VK_DOWN)
				++nIndex;
			else
				--nIndex;

			if (nIndex >= GetCount() || nIndex < 0)
				return S_OK;

			m_pListBox->SetCurSel(nIndex);
			return S_OK;
		}
	}
		break;
	case WM_CHAR: 
	{
		if (wParam == VK_SPACE) 
		{
			int nIndex = CallWindowProc(m_pOldWndProc, hWnd, LB_GETCURSEL, wParam, lParam);
			CRect rcItem;
			m_pListBox->GetItemRect(nIndex, rcItem);
			m_pListBox->InvalidateRect(rcItem, FALSE);			
			SetCheck(nIndex, !GetCheck(nIndex));
			return S_OK;
		}		

		break;
	}
	case WM_LBUTTONDOWN: 
	{
		CRect rcClient;
		m_pListBox->GetClientRect(rcClient);
		CPoint point;
		point.x = LOWORD(lParam);
		point.y = HIWORD(lParam);		
		if (rcClient.PtInRect(point))
		{
			int nItemHeight = GetItemHeight(0);
			int nTopIndex = GetTopIndex();
			int nIndex = nTopIndex + point.y / nItemHeight;
			CRect rcItem; 
			m_pListBox->GetItemRect(nIndex, rcItem);
			if (PtInRect(rcItem, point)) 
			{
				m_pListBox->InvalidateRect(rcItem, FALSE);
				SetCheck(nIndex, !GetCheck(nIndex));
			}
		}		
		break;
	}
	case WM_LBUTTONUP:
		return S_OK;
	case WM_MOUSEMOVE:
		TRACE("%s::WM_MOUSEMOVE(%d)\n", GetRuntimeClass()->m_lpszClassName, GetTickCount());
		CRect rcClient;
		m_pListBox->GetClientRect(rcClient);
		CPoint point;
		point.x = LOWORD(lParam);
		point.y = HIWORD(lParam);
		if (!rcClient.PtInRect(point))
			return E_FAIL;

		break;
	}

	return CallWindowProc(m_pOldWndProc, hWnd, message, wParam, lParam);
}

BOOL CSWCheckComboBox::Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID)
{
	// 动态创建的控件才会到这里来,否则需要手动修改这些属性
	dwStyle &= ~0x0F;
	dwStyle |= CBS_DROPDOWNLIST;
	dwStyle |= CBS_OWNERDRAWFIXED;
	dwStyle |= CBS_HASSTRINGS;
	return CComboBox::Create(dwStyle, rect, pParentWnd, nID);
}

LRESULT CSWCheckComboBox::OnCtlColorListBox(WPARAM wParam, LPARAM lParam)
{
	if (m_pListBox == NULL)
	{
		HWND hWnd = (HWND)lParam;
		if (hWnd != 0 && hWnd != m_hWnd) 
		{			
			if (!SetProp(hWnd, _T("CComboBoxHelper"), (HANDLE)this))
				return E_FAIL;

			m_pOldWndProc = (WNDPROC)GetWindowLong(hWnd, GWL_WNDPROC);
			SetWindowLong(hWnd, GWL_WNDPROC, (LONG)CSWCheckComboBox::ListBoxWindowProc);
			m_pListBox = (CListBox*)CWnd::FromHandle(hWnd);
		}
	}

	return DefWindowProc(WM_CTLCOLORLISTBOX, wParam, lParam);
}

void CSWCheckComboBox::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
	TRACE("%s::DoPaint(%d)\n", this->GetRuntimeClass()->m_lpszClassName, GetTickCount());
	CDC dc;
	if (!dc.Attach(lpDrawItemStruct->hDC))
		return;

	CRect rcBitmap = lpDrawItemStruct->rcItem;
	CRect rcText = lpDrawItemStruct->rcItem;
	CString strText;

	int nCheck = 0;
	if ((LONG)lpDrawItemStruct->itemID < 0)
	{
		CString strSelText = UpdateCheckedText(FALSE);
		strText = strSelText;
		nCheck = 0;
	}
	else 
	{
		GetLBText(lpDrawItemStruct->itemID, strText);
		nCheck = 1 + (GetItemData(lpDrawItemStruct->itemID) != 0);
		TEXTMETRIC metrics;
		dc.GetTextMetrics(&metrics);
		rcBitmap.left = 0;
		rcBitmap.right = rcBitmap.left + metrics.tmHeight + metrics.tmExternalLeading + 6;
		rcBitmap.top += 1;
		rcBitmap.bottom -= 1;
		rcText.left = rcBitmap.right;
	}

	if (nCheck > 0) 
	{
		dc.SetBkColor(GetSysColor(COLOR_WINDOW));
		dc.SetTextColor(GetSysColor(COLOR_WINDOWTEXT));
		UINT nState = DFCS_BUTTONCHECK;
		if (nCheck > 1)
			nState |= DFCS_CHECKED;
		dc.DrawFrameControl(rcBitmap, DFC_BUTTON, nState);
	}

	if (lpDrawItemStruct->itemState & ODS_SELECTED) 
	{
		dc.SetBkColor(GetSysColor(COLOR_HIGHLIGHT));
		dc.SetTextColor(GetSysColor(COLOR_HIGHLIGHTTEXT));
	}
	else 
	{
		dc.SetBkColor(GetSysColor(COLOR_WINDOW));
		dc.SetTextColor(GetSysColor(COLOR_WINDOWTEXT));
	}

	dc.ExtTextOut(0, 0, ETO_OPAQUE, &rcText, 0, 0, 0);
	dc.DrawText(' ' + strText, strText.GetLength() + 1, &rcText, DT_SINGLELINE | DT_VCENTER | DT_END_ELLIPSIS);
	if ((lpDrawItemStruct->itemState & (ODS_FOCUS | ODS_SELECTED)) == (ODS_FOCUS | ODS_SELECTED))
		dc.DrawFocusRect(&rcText);
	dc.Detach();
}

void CSWCheckComboBox::CheckAll(int nCheck)
{
	for (int nIndex = 0; nIndex < GetCount(); nIndex++)
		SetCheck(nIndex, nCheck);
}

LRESULT CSWCheckComboBox::OnGetText(WPARAM wParam, LPARAM lParam)
{
	CString strSelText = UpdateCheckedText(FALSE);
	if (lParam == 0)
		return S_OK;

	lstrcpyn((LPSTR)lParam, strSelText, (int)wParam);
	return strSelText.GetLength();
}

LRESULT CSWCheckComboBox::OnGetTextLength(WPARAM, LPARAM)
{
	CString strSelText = UpdateCheckedText(FALSE);
	return strSelText.GetLength();
}

CString CSWCheckComboBox::UpdateCheckedText(BOOL bReupdate/* = TRUE*/)
{
	static CString s_strSelText = _T("");
	if (bReupdate)
	{
		// 获取系统默认分隔符
		TCHAR szBuffer[10] = { 0 };
		GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SLIST, szBuffer, sizeof(szBuffer));
		CString strSeparator = szBuffer;
		if (strSeparator.GetLength() == 0)
			strSeparator = ';';
		strSeparator.TrimRight();
		strSeparator += ' ';
		CString strSelText;
		for (int nIndex = 0; nIndex < GetCount(); nIndex++)
		{
			if (GetItemData(nIndex)) {
				CString strItem;
				GetLBText(nIndex, strItem);
				if (!strSelText.IsEmpty())
					strSelText += strSeparator;
				strSelText += strItem;
			}
		}

		s_strSelText = strSelText;
	}

	return s_strSelText;
}

int CSWCheckComboBox::SetCheck(int nIndex, int nCheck, BOOL bRepaint/* = TRUE*/)
{
	int nItem = SetItemData(nIndex, (DWORD)nCheck);
	if (nItem == CB_ERR)
		return CB_ERR;

	UpdateCheckedText();
	if (bRepaint)
	{
		Invalidate(FALSE);
		RedrawWindow();
	}

	return nItem;
}

BOOL CSWCheckComboBox::GetCheck(int nIndex)
{
	return (BOOL)GetItemData(nIndex);
}

调用源码::OnInitDialog(): 

//选择下拉框
m_combobox3.SubclassDlgItem(IDC_COMBO5, this);
m_combobox3.AddString(_T("苹果"));
m_combobox3.AddString(_T("橘子"));

 

  • 1
    点赞
  • 1
    评论
  • 5
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值