禁用时显示自定义控件提示
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
以防止触发鼠标事件。)
我编写了一个自定义控件 (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
以防止触发鼠标事件。)