You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何点击自定义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自定义按钮的标准实现方式:

  1. 打开资源编辑器,找到对应按钮控件的属性,将Owner Draw选项设置为True。如果是动态创建的按钮,创建时添加BS_OWNERDRAW风格。
  2. 在CImageButton类中重写DrawItem虚方法,将你之前写在OnPaint中的绘制逻辑迁移到该方法中。
  3. 可选:重写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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.01 13:36:03