如何在C++Builder中子类化TButton实现自绘?
你遇到的核心问题是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
相关产品推荐
相关产品推荐

