为何点击自定义MFC CImageButton时会触发父类CButton的OnPaint方法
问题描述
我正在MFC的CButton类的子类中实现OnPaint()方法,代码如下:
class CImageButton : public CButton public: using CButton::CButton; protected: afx_msg void OnPaint(); DECLARE_MESSAGE_MAP() }; BEGIN_MESSAGE_MAP(CImageButton, CButton) ON_WM_PAINT() END_MESSAGE_MAP() void CImageButton::OnPaint() { /* 绘制图片,不调用CButton::OnPaint() */ }
随后我创建了一个对话框,在资源编辑器中为对话框添加了按钮控件,在对话框声明中添加:
CImageButton m_btn;
并在DoDataExchange()中添加:
DDX_Control(pdc, IDC_BUTTON, m_btn);
我认为已经完成了自定义绘制的所有配置,运行时对话框打开初始阶段按钮绘制正常,按我实现的逻辑没有绘制按钮边框,只显示自定义图片。但点击按钮时,自定义图片消失,同时出现按钮默认边框和我在VS资源编辑器中设置的按钮文本,看起来是父类的OnPaint()方法被调用了。我在自定义OnPaint入口加了调试日志,确认点击时该方法确实没有被触发。而当我将焦点切换到其他控件时,按钮又会重新绘制显示自定义图片。
问题原因
MFC标准按钮控件在交互状态(按下、悬浮、获得/失去焦点)变更时,默认不会触发WM_PAINT消息,而是直接调用DrawItem方法完成绘制,只有在窗口初始加载、无效区域主动刷新时才会调用OnPaint,这就是点击时自定义绘制逻辑不生效的原因。
修复方案
方案一:使用自绘模式(推荐)
这是MFC自定义按钮的标准实现方式:
- 打开资源编辑器,找到对应按钮控件的属性,将Owner Draw选项设置为
True。如果是动态创建的按钮,创建时添加BS_OWNERDRAW风格。 - 在
CImageButton类中重写DrawItem虚方法,将你之前写在OnPaint中的绘制逻辑迁移到该方法中。 - 可选:重写
OnEraseBkgnd方法返回TRUE,避免绘制闪烁。
修改后的完整代码示例:
class CImageButton : public CButton public: using CButton::CButton; protected: afx_msg void OnPaint(); virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) override; afx_msg BOOL OnEraseBkgnd(CDC* pDC); DECLARE_MESSAGE_MAP() }; BEGIN_MESSAGE_MAP(CImageButton, CButton) ON_WM_PAINT() ON_WM_ERASEBKGND() END_MESSAGE_MAP() void CImageButton::OnPaint() { CPaintDC dc(this); // 你的自定义绘制代码,也可以直接调用DrawItem触发统一绘制逻辑 } void CImageButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) { CDC dc; dc.Attach(lpDrawItemStruct->hDC); // 这里写入你的自定义绘制逻辑,可以通过lpDrawItemStruct->itemState判断按钮状态 // 示例:判断按钮是否被按下 if (lpDrawItemStruct->itemState & ODS_SELECTED) { // 绘制按下状态的样式 } else { // 绘制普通状态的样式 } dc.Detach(); } BOOL CImageButton::OnEraseBkgnd(CDC* pDC) { return TRUE; // 禁止系统擦除背景,避免闪烁 }
方案二:主动触发重绘
如果你不想修改按钮属性使用自绘模式,可以重写所有交互相关的消息处理函数,在状态变化时主动触发OnPaint调用:
// 在CImageButton类的消息映射中添加以下条目 ON_WM_LBUTTONDOWN() ON_WM_LBUTTONUP() ON_WM_SETFOCUS() ON_WM_KILLFOCUS() // 对应的实现 void CImageButton::OnLButtonDown(UINT nFlags, CPoint point) { CButton::OnLButtonDown(nFlags, point); Invalidate(); // 主动触发重绘 } void CImageButton::OnLButtonUp(UINT nFlags, CPoint point) { CButton::OnLButtonUp(nFlags, point); Invalidate(); } void CImageButton::OnSetFocus(CWnd* pOldWnd) { CButton::OnSetFocus(pOldWnd); Invalidate(); } void CImageButton::OnKillFocus(CWnd* pNewWnd) { CButton::OnKillFocus(pNewWnd); Invalidate(); }
内容的提问来源于stack exchange,提问作者Serge Roussak
相关产品推荐
相关产品推荐

