如何更改启用主题的控件的字体颜色?

9
是的,这又是一个问题:
如何在启用主题的应用程序中使用Delphi7-> Delphi2007更改TCheckBox(或任何已处理控件)的字体颜色?
在互联网上和本站上阅读了很多后,我发现有四种答案:
1.最受欢迎的(甚至来自QC):您无法更改,因为Microsoft设计了它。
2.创建一个组件,让您按照所需方式进行绘制。
3.购买昂贵的组件集,以按照所需方式进行绘制。
4.不要使用主题。
但是,我对此仍然不满意。
在表单上为用户提供属性/数据状态的彩色反馈,对我来说似乎是合理的。
然后我刚刚安装了MSVC#2008 Express版,惊奇地发现他们可以更改字体的颜色(检查框的ForeColor属性)。那么现在呢?
看起来这并不像“Microsoft设计的那样”。那么现在问题又来了:
如何在启用主题的应用程序中使用Delphi 7到Delphi 2007更改TCheckBox(或任何已处理控件)的字体颜色?

你觉得 Visual Studio 的开发人员为什么不直接使用选项2呢? - Rob Kennedy
好的,我在尝试复制C#中的“这是由微软设计的”时,并不需要这样做。 我将ForeColor设置为红色,然后我的复选框标题就变成了红色。 - Edouard Westphal
5个回答

4
这需要一些微调才能成为完美的解决方案,但对我有用:
向你的复选框组件添加2个方法。
    FOriginalCaption: string;
    _MySetCap: Boolean;
    procedure WMPaint(var msg: TWMPaint); message WM_PAINT;
    procedure CMTextChanged(var Message: TMessage); message CM_TEXTCHANGED;

并且这样实现:
procedure TMyCheckbox.CMTextChanged(var Message: TMessage);
begin
  inherited;
  if _MySetCap then Exit;
  FOriginalCaption := Caption;
end;

procedure TMyCheckbox.WMPaint(var msg: TWMPaint);
var
  BtnWidth: Integer;
  canv: TControlCanvas;
begin
  BtnWidth := GetSystemMetrics(SM_CXMENUCHECK);

  _MySetCap := True;
  if not (csDesigning in ComponentState) then
    Caption := '';
  _MySetCap := False;
  inherited;
  canv := TControlCanvas.Create;
  try
    canv.Control := Self;
    canv.Font := Font;
    SetBkMode(canv.Handle, Ord(TRANSPARENT));
    canv.TextOut(BtnWidth + 1, 2, FOriginalCaption);
  finally
    canv.Free;
  end;
end;

我认为这应该被标记为答案,干得好! - Nickolas de Luca Alberton

2

噢,但是你能做到!

只需要在表单声明之前加上以下内容:

TCheckBox = class(StdCtrls.TCheckBox)
public
  procedure CNCtlColorStatic(var Message: TWMCtlColorStatic); message CN_CTLCOLORSTATIC;
end;

这里对 TCheckBox 进行重新声明,现在作为类型从你的表单 DFM 中流式传输到运行时。现在按照以下方式实现消息:

procedure TCheckBox.CNCtlColorStatic(var Message: TWMCtlColorStatic);
begin
  SetTextColor(Message.ChildDC, ColorToRGB(clRed)); // or RGB(255,0,0));
  SetBkMode(Message.ChildDC, TRANSPARENT);
  Message.Result := GetStockObject(NULL_BRUSH);
end;

这会捕获 WM_CTLCOLORSTATIC 消息并将文本颜色更改为红色。在非主题模式下,我使用 WinXP 经典模式可以正常工作,但在主题模式下无法正常工作。

您应该知道,为了让主题控件向您发送此消息,控件应向主题绘制 API 提供 DTPB_USECTLCOLORSTATIC 标志。不幸的是,这不是默认行为,我也不知道如何做到这一点。还要看看 这个 问题。


2
这是我在我的应用程序中解决此问题的方法:
  1. 从复选框中删除标题并使其变小 - 只有足够大以显示实际复选框而没有标题。
  2. 在复选框旁边放置TLabel以充当标题。
  3. 在标签的OnClick事件中,切换复选框的状态。
  4. 如果标签具有Alt +字母键盘加速器,请使表单处理CM_DIALOGCHAR消息。从TLabel源代码中复制消息处理程序,并添加一行以切换复选框的状态。
它不是真正的复选框,而且比我想要的更麻烦,但它在我的应用程序中运行得足够好(只有一个需要进行此处理的复选框)。

0

选项5. 使用您喜欢的控件作为基本选项,并覆盖控件中的所有绘制消息(是的,您可以将其称为组件,但控件是可见组件的名称,因此您最好使用它)。只需捕获WM_PAINT,可能是WM_NCPAINT,您就可以以自己的风格绘制控件。至少您可以重用控件的全部功能。只要您不更改布局,仅更改颜色,您就不需要更改hittests mousedowns、up moves等等。

注意:我有覆盖TCustomEdit以允许各种颜色、背景文本、额外按钮等的经验。花了相当长的时间才弄对了,并阅读了来自MSDn和KB的所有文档,以确保控件按照我想要的方式运行。


我理解为选项5不是,而是选项2a,我的选项2是选项2b ;^) !但是为什么C#不需要重写wm_paint方法来更改复选框组件的字体颜色? - Edouard Westphal
@Edouard。名字有什么关系..关于C#,它不需要它:C#有自己的控制层(微软每隔几年为.NET环境发明一个新的控制层),Delphi使用自Windows 95以来存在的相同控件。因此更加稳定,但有时你会为此付出代价,一些简单的东西变得更加困难;GDI已经老旧了,但我认为在10年内GDI仍然存在,那么你应该怎么处理你的WinForms应用程序呢?考虑一下! - Ritsaert Hornstra
我知道GDI相当古老,我开始使用它是在我的第一步Borland Object Pascal上,大约是1988年的Windows 2。你的意思是:C#可以做到这一点,因为他们使用的控件与Delphi使用的完全不同,而主题启用了?由于主题负责绘制新的东西,我会认为Delphi正在使用与C#使用的相同组件(或至少库)。 - Edouard Westphal
顺便提一句,我现在也在Access2003上测试了一下:创建一个表单,添加一个复选框,然后将前景色设置为红色,这里也可以正常工作(带主题的组件+有色复选框)!!!我只是确信Access2003设计用于使用比Delphi2007更旧的GDI。 - Edouard Westphal
1
@Edouard:Access使用自己的自定义控件库(选项2)。 关于C#:这取决于你所说的哪个库(WinForms、WPF等)。 但如果是WinForms,则它会包装并可能扩展(选项2!)内置的Windows控件。 如果是类似WPF之类的东西,那么它就是一个完全不同的控件库。 - David
为什么您不喜欢已经得到的答案?您列出的选项是准确和有效的 - 您不能只是说“我不喜欢它们,所以我会再问一遍。” - David

0

这段代码是从 @FLICKER 改进的代码,支持 BidiMode 和文本的正确位置:

interface
  TMSCheckBox = class(TCheckBox)
  private
    FOriginalCaption: string;
    _MySetCap: Boolean;
    procedure WMPaint (var Message: TWMPaint); message WM_PAINT;
    procedure CMTextChanged(var Message: TMessage); message CM_TEXTCHANGED;
  end;

implementation

procedure TMSCheckBox.CMTextChanged(var Message: TMessage);
begin
  inherited;
  if _MySetCap then Exit;
  FOriginalCaption := Caption;
end;

procedure TMSCheckBox.WMPaint(var Message: TWMPaint);
const
  SPACE   :Integer = 2;
var
  txtW, txtH, txtX,
  BtnWidth: Integer;
  canv: TControlCanvas;
begin
  BtnWidth := GetSystemMetrics(SM_CXMENUCHECK);

  _MySetCap := True;
  if not (csDesigning in ComponentState) then
    Caption := '';
  _MySetCap := False;
  inherited;
  canv := TControlCanvas.Create;
  try
    canv.Control := Self;
    canv.Font := Font;
    txtW:= canv.TextWidth(FOriginalCaption);
    txtH:= canv.TextHeight(FOriginalCaption);
    if BiDiMode in [bdRightToLeft, bdRightToLeftReadingOnly] then
      txtX:= Width - BtnWidth - SPACE - txtW
    else
      txtX:= BtnWidth + SPACE;
    SetBkMode(canv.Handle, Ord(TRANSPARENT));
    canv.TextOut(txtX, (Height - txtH) div 2 + 1, FOriginalCaption);
  finally
    canv.Free;
  end;
end;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接