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

如何在C++Builder中子类化TButton实现自绘?

解决C++Builder中TButton设置BS_OWNERDRAW后收不到WM_DRAWITEM的问题

你遇到的核心问题是WM_DRAWITEM消息不会发送给按钮自身,而是发送给按钮的父窗口(即你的Form),所以在按钮的WindowProc里根本捕获不到这条消息。

另外,虽然你已经通过SetWindowLong设置了BS_OWNERDRAW样式,但修改窗口样式后需要强制系统更新控件的外观状态,否则样式变更可能不会完全生效。

修正步骤

  • 将WM_DRAWITEM的处理逻辑移到Form的窗口过程中,判断消息对应的控件是否是目标按钮bTest。
  • 设置样式后调用SetWindowPos强制刷新控件的框架状态。

修正后的代码示例

__fastcall TMainForm::TMainForm(TComponent* Owner)
  : TForm(Owner)
{
  // 设置按钮的BS_OWNERDRAW样式
  LONG style = GetWindowLong(bTest->Handle, GWL_STYLE);
  style |= BS_OWNERDRAW;
  SetWindowLong(bTest->Handle, GWL_STYLE, style);
  
  // 强制刷新控件样式,让系统识别样式变更
  SetWindowPos(bTest->Handle, NULL, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE);
}
//---------------------------------------------------------------------------

void __fastcall TMainForm::WndProc(TMessage &AMsg)
{
  if (AMsg.Msg == WM_DRAWITEM)
  {
    LPDRAWITEMSTRUCT dis = reinterpret_cast<LPDRAWITEMSTRUCT>(AMsg.LParam);
    // 确认是目标按钮的绘制消息
    if (dis->hwndItem == bTest->Handle)
    {
      Beep();
      // 示例:自定义绘制按钮背景和文字
      HBRUSH brush = CreateSolidBrush(RGB(255, 0, 0));
      FillRect(dis->hDC, &dis->rcItem, brush);
      DeleteObject(brush);
      
      DrawText(dis->hDC, bTest->Caption.c_str(), -1, &dis->rcItem, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
      
      // 标记消息已处理
      AMsg.Result = TRUE;
      return;
    }
  }
  // 其他消息交给默认处理逻辑
  TForm::WndProc(AMsg);
}
//---------------------------------------------------------------------------

补充说明

  • 如果你的Form尚未重载WndProc,需要在Form的头文件中声明void __fastcall WndProc(TMessage &AMsg);。
  • 自定义绘制时建议处理按钮的所有状态(正常、按下、禁用、聚焦等),保证按钮的交互反馈符合用户预期。

内容的提问来源于stack exchange,提问作者Kevin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 20:10:05