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

时间:2015-02-13 03:37:59

标签: delphi custom-controls vcl

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

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

3 个答案:

答案 0 :(得分:5)

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

答案 1 :(得分:3)

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

首先,要启用窗口句柄(WinAPI),您需要从窗口样式中删除WS_DISABLED样式,或使用EnableWindow。此修改不会同步VCL的Enabled属性(与其他方式不同:设置Enabled属性dóescallEnableWindow),这就是为什么会这样做。

但启用窗口句柄可让所有鼠标消息通过,因此您必须在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显示提示的原因是因为它从其父级的CM_MOUSEENTER收到WndProc,尽管同一个父级通过{{阻止所有其他鼠标输入1}}以防止鼠标事件被触发。)

答案 2 :(得分:1)

实际上,当您禁用控件时,您甚至无法调用Winproc的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.

YES!正确:丑陋的黑客攻击和违反所有设计模式但是通过这个小例子你可以看到消息循环是如何工作的,这是一种测试某些东西的简单方法。

PS:我将此作为答案,因为您无法在评论中格式化文本:D