MFC自定义按钮实现

MFC中要实现自定义按钮,首先要创建一个类并继承自CButton。我这里创建的类名为CMainButton

class CMainButton : public CButton
{
	DECLARE_DYNAMIC(CMainButton)

public:
	CMainButton(UINT nID,CRect rcWnd,CWnd* pParent=nullptr);//nID为按钮ID,rcWnd为按钮位置
	virtual ~CMainButton();
	void SetInvalid();   //调用了_SetInvalidPt(m_clickPoint)
protected:
	bool IsValidPt(const CPoint& pt) const;  //pt是否有效,当pt的x和y都大于等于0时,程序认为有效。
        // 主要为m_clickPoint服务,因为是在客户区点击,故不可能点击到x小于0或y小于0的位置
        // 返回true表示之前点击了鼠标左键
	int GetFontPtSize() const;//自定义实现的按钮默认可以调整大小,根据按钮大小返回自适应内容字体大小
	void _SetInvalidPt(CPoint& pt); //让pt无效
	CString m_strOne;  //单态(m_bOne为true)下,显示的窗口内容

	/// <summary>
	/// 3-triState
	/// </summary>
	CString m_strLeft;   //三态(m_bOne为false)下,最左边显示的窗口内容
	CString m_strCenter; //三态(m_bOne为false)下,中间显示的窗口内容
	CString m_strRight;  //三态(m_bOne为false)下,最右边显示的窗口内容
	BOOL m_bOne;    //m_bOne为true,表示单态,为false,表示多态
	CRect m_rcLeft;  //三态(m_bOne为false)下,最左边显示的窗口区域
	CRect m_rcCenter;//三态(m_bOne为false)下,中间显示的窗口区域
	CRect m_rcRight;//三态(m_bOne为false)下,最右边显示的窗口区域
protected:
	DECLARE_MESSAGE_MAP()
public:
	virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
private:
	CPoint m_clickPoint; // 鼠标点击位置
public:
	afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
};

要实现自绘,还需要在调用Create时传窗口风格,传递的风格中要加上BS_OWNERDRAW风格,如果没有BS_OWNERDRAW风格,窗口不会调用DrawItem实现程序员的自绘操作。在我实现的控件中,将类与窗口绑定,类创建时窗口也创建了,类消亡时窗口也销毁了。实现如下:

CMainButton::CMainButton(UINT nID, CRect rcWnd, CWnd* pParent)
{
	m_strOne = _T("选择");
	m_strLeft = _T("分割文件");
	m_strCenter = _T("取消");
	m_strRight = _T("合并文件");
	DWORD dwStyle =WS_VISIBLE | BS_PUSHBUTTON | BS_OWNERDRAW;
	if (pParent){
		dwStyle |= WS_CHILD;
	}
	Create(m_strOne, dwStyle, rcWnd, pParent, nID);
	m_bOne = TRUE;
	SetInvalid();
}
CMainButton::~CMainButton()
{
	DestroyWindow();
}

加上了这个风格后,下面我们来看看自绘操作DrawItem

void CMainButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
	CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC); // 获取设备上下文
	RECT rect = lpDrawItemStruct->rcItem; // 获取绘制区域
	UINT state = lpDrawItemStruct->itemState; // 获取按钮状态

	CFont wndFont;
	int ptFontSize = GetFontPtSize(); //根据窗口大小计算窗口内容文字大小
	wndFont.CreatePointFont(ptFontSize, _T("微软雅黑"));
	CFont* pOldFont = pDC->SelectObject(&wndFont);
	COLORREF buttonColor = GetSysColor(COLOR_BTNFACE); //获取按钮背景色
	if (m_bOne){//单态按钮状态下的绘制
	  if (state & ODS_SELECTED) {
		pDC->DrawText(m_strOne, &rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE); // 绘制文本
		pDC->DrawEdge(&rect, EDGE_SUNKEN, BF_RECT); // 按钮按下时的边框
		if (IsValidPt(m_clickPoint)){
		  m_bOne = FALSE;
		}
	  }
	  else {
		pDC->FillSolidRect(&rect, buttonColor);
		pDC->DrawText(m_strOne, &rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE); // 绘制文本
		pDC->DrawEdge(&rect, EDGE_RAISED, BF_RECT); // 按钮未按下时的边框
	  }
	}
	else {
		int width = rect.right - rect.left;
		int perWidth = width / 3;
		if (state & ODS_SELECTED) {
			if (m_rcCenter.PtInRect(m_clickPoint)) {
				m_bOne = TRUE;
			}
			if (IsValidPt(m_clickPoint)) { //说明经过了LButtonDown,左键单机了
				for (int i = 0; i < 3; i++) {
					CRect rc(rect.left + perWidth *i, rect.top, rect.left + perWidth * (i + 1), rect.bottom);
					if (i == 0) {
						pDC->DrawText(m_strLeft, &rc, DT_CENTER | DT_VCENTER | DT_SINGLELINE); // 绘制文本
					}
					else if (i == 1) {
						pDC->DrawText(m_strCenter, &rc, DT_CENTER | DT_VCENTER | DT_SINGLELINE); // 绘制文本
					}
					else if (i == 2) {
						pDC->DrawText(m_strRight, &rc, DT_CENTER | DT_VCENTER | DT_SINGLELINE); // 绘制文本
					}
					if (rc.PtInRect(m_clickPoint)) {
						pDC->DrawEdge(&rc, EDGE_SUNKEN, BF_RECT); // 按钮按下时的边框
						_SetInvalidPt(m_clickPoint);
					}
				}
			}
		}
		else {
			for (int i = 0; i < 3; i++) {
				CRect rc(rect.left + perWidth * i, rect.top, rect.left + perWidth * (i + 1), rect.bottom);
				if (i==0){
					pDC->DrawText(m_strLeft, &rc, DT_CENTER | DT_VCENTER | DT_SINGLELINE); // 绘制文本
					m_rcLeft = rc;
				}
				else if (i == 1) {
					pDC->DrawText(m_strCenter, &rc, DT_CENTER | DT_VCENTER | DT_SINGLELINE); // 绘制文本
					m_rcCenter = rc;
				}
				else if (i == 2) {
					pDC->DrawText(m_strRight, &rc, DT_CENTER | DT_VCENTER | DT_SINGLELINE); // 绘制文本
					m_rcRight = rc;
				}
				pDC->DrawEdge(&rc, EDGE_RAISED, BF_RECT); // 按钮未按下时的边框
			}
		}
	}
}
posted @   DJFFding  阅读(119)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· .NET Core 中如何实现缓存的预热?
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 如何调用 DeepSeek 的自然语言处理 API 接口并集成到在线客服系统
点击右上角即可分享
微信分享提示