禁用时显示自定义控件提示

Show custom control hint when disabled

我编写了一个自定义控件 (TCustomControl),它在悬停时显示标准的内置提示。但是,当控件被禁用时,提示不会显示。但是,TSpeedButton 在禁用时确实会显示提示,因此必须有一种方法可以在我的控件中执行相同的操作。

当我的控件被禁用时,我需要做什么才能显示提示?

标准提示机制基于鼠标消息。派生自 TWinControl(包括 TCustomControl)的控件在禁用时不接收鼠标消息,并且提示系统在内部忽略禁用的窗口控件。 TSpeedButton 源自 TGraphicControl 而不是 TWinControl,因此不受这些限制。

实际上,当您的控件被禁用时,您控件的 Winproc 甚至不会被调用。这个小演示是为了更好地理解消息循环。

在窗体上放置一个TPanel,并向窗体添加一个Double clickEvent。然后试试这个代码:

unit Unit39;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls;

type
  TPanel = class(ExtCtrls.TPanel)
  protected
    procedure WndProc(var Message: TMessage); override;
  end;

  TForm39 = class(TForm)
    Panel1: TPanel;
    procedure FormDblClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form39: TForm39;

implementation

{$R *.dfm}

{ TPanel }

procedure TPanel.WndProc(var Message: TMessage);
begin
  inherited;
  Application.MainForm.Caption := FloatToStr(now);
end;

procedure TForm39.FormDblClick(Sender: TObject);
begin
  Panel1.Enabled := not Panel1.Enabled;
end;

end.

是的!正确:丑陋的 hack 和违反所有设计模式,但通过这个小示例,您可以看到消息循环的工作原理,这是测试某些东西的非常简单的方法。

PS:我把这个作为答案,因为你不能在评论中格式化你的文字:D

您需要启用 window 句柄才能获得开始显示提示的 WM_MOUSEMOVE。这有一些影响。

首先,要启用window句柄(WinAPI),您需要从window样式中删除WS_DISABLED样式,或者使用EnableWindow。此修改不会同步 VCL 的 Enabled 属性(与相反的方式不同:设置 Enabled 属性 调用 EnableWindow),这就是它起作用的原因.

但是启用 window 句柄允许所有鼠标消息通过,因此您必须阻止它们并在 WM_MOUSEMOVE:

上手动激活提示
type
  TMyControl = class(TCustomControl)
  private
    FDisabledHint: Boolean;
    procedure CheckEnabled;
    procedure SetDisabledHint(Value: Boolean);
    procedure CMEnabledchanged(var Message: TMessage);
      message CM_ENABLEDCHANGED;
  protected
    procedure CreateParams(var Params: TCreateParams); override;
    procedure SetParent(AParent: TWinControl); override;
    procedure WndProc(var Message: TMessage); override;
  published
    property DisabledHint: Boolean read FDisabledHint write SetDisabledHint;
  end;

{ TMyControl }

procedure TMyControl.CheckEnabled;
begin
  if DisabledHint and HasParent and (not Enabled) and
      not (csDesigning in ComponentState) then
    EnableWindow(Handle, True);
end;

procedure TMyControl.CMEnabledchanged(var Message: TMessage);
begin
  inherited;
  CheckEnabled;
end;

procedure TMyControl.CreateParams(var Params: TCreateParams);
begin
  inherited CreateParams(Params);
  if DisabledHint and not Enabled then
    Params.Style := Params.Style and (not WS_DISABLED);
end;

procedure TMyControl.SetDisabledHint(Value: Boolean);
begin
  if FDisabledHint <> Value then
  begin
    FDisabledHint := Value;
    CheckEnabled;
  end;
end;

procedure TMyControl.SetParent(AParent: TWinControl);
begin
  inherited SetParent(AParent);
  CheckEnabled;
end;

procedure TMyControl.WndProc(var Message: TMessage);
begin
  if not Enabled and DisabledHint and (Message.Msg = WM_MOUSEMOVE) then
    Application.HintMouseMessage(Self, Message);
  if Enabled or (Message.Msg < WM_MOUSEFIRST) or
      (Message.Msg > WM_MOUSELAST) then
    inherited WndProc(Message);
end;

我检查了 TabStop 属性 的工作情况,这个解决方案不会干扰它。但是要注意我还没有想到的问题。

(此外,禁用的 TControl 显示提示的原因是因为它从其父级的 WndProc 接收到 CM_MOUSEENTER,尽管同一个父级阻止了所有其他鼠标输入IsControlMouseMsg 以防止触发鼠标事件。)