正如问题评论中所确定的那样,提示不会无限期地保留在屏幕上,但实际上,一旦隐藏,它就会不断地重新显示。
原因是,VCL 假定提示控件是一个子窗口,那是因为它是Parent
财产并非为零。在问题中的代码的情况下,虽然月历floats通过将其转变为弹出窗口,其父窗口仍然是 VCL 所知的窗体。这会导致提示矩形的计算ActivateHint
应用程序的过程出错。另一方面,HintMouseMessage
应用程序的过程并不关心控件是否有父级。接下来发生的情况是,虽然您没有在控件上移动鼠标指针,但 VCL 会推断鼠标指针不断离开提示边界然后重新进入。
这是该问题的简化再现:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, Vcl.StdCtrls;
type
TPanel = class(vcl.extctrls.TPanel)
protected
procedure CreateParams(var Params: TCreateParams); override;
end;
TForm1 = class(TForm)
Button1: TButton;
Panel1: TPanel;
procedure FormCreate(Sender: TObject);
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
{ TPanel }
procedure TPanel.CreateParams(var Params: TCreateParams);
begin
inherited;
Params.Style := WS_POPUPWINDOW or WS_THICKFRAME;
end;
{ TForm1 }
procedure TForm1.FormCreate(Sender: TObject);
begin
Button1.Hint := 'Button1';
Panel1.Hint := 'Panel1';
ShowHint := True;
Application.HintHidePause := 1000;
Left := 0;
Top := 0;
Panel1.ParentBackground := False;
Panel1.Left := 0;
Panel1.Height := 50;
Panel1.Top := Top + Height;
end;
end.
在上面的代码中,按钮的提示会在超时时隐藏,而面板的提示在隐藏后会重新显示。我特意将窗口定位到它们的位置,以便您可以在激活提示时观察指针位置的意义。如果您将鼠标指针从下方输入面板,提示将仅显示一次,然后隐藏。但是,如果您从上面进入面板,您就会看到问题。
修复很简单,您可以修改提示矩形CM_HINTSHOW
消息处理程序。由于控制是浮动的,因此不需要复杂的计算。相应地修改了复制案例,这也修复了问题中的日历:
type
TPanel = class(vcl.extctrls.TPanel)
protected
procedure CreateParams(var Params: TCreateParams); override;
procedure CMHintShow(var Message: TCMHintShow); message CM_HINTSHOW;
end;
TForm1 = class(TForm)
...
{ TPanel }
procedure TPanel.CMHintShow(var Message: TCMHintShow);
begin
inherited;
if (GetAncestor(Handle, GA_ROOT) = Handle) and Assigned(Parent) then
Message.HintInfo.CursorRect := Rect(0, 0, Width, Height);
end;
至于问题2,不幸的是似乎没有设计自定义提示窗口可定位。提示窗口是本地创建的,没有巧妙的方法来获取它或以任何其他方式指定其位置。我能想到的唯一方法是重写自定义提示的绘制方法之一,该方法将提示窗口公开为参数。因此,我们可以在提示窗口收到绘画消息后立即重新定位它。
这是一个工作示例(对于正常的(非浮动) 控制):
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, Vcl.StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
procedure FormCreate(Sender: TObject);
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
type
TMyCustomHint = class(TCustomHint)
private
FControl: TControl;
public
procedure NCPaintHint(HintWindow: TCustomHintWindow; DC: HDC); override;
end;
procedure TMyCustomHint.NCPaintHint(HintWindow: TCustomHintWindow; DC: HDC);
var
Pt: TPoint;
begin
Pt := FControl.ClientToScreen(Point(0, 0));
SetWindowPos(HintWindow.Handle, 0, Pt.X, Pt.Y + FControl.Height, 0, 0,
SWP_NOSIZE or SWP_NOZORDER or SWP_NOACTIVATE);
inherited;
end;
//--------
procedure TForm1.FormCreate(Sender: TObject);
begin
ShowHint := True;
Button1.Hint := 'button1 hint';
Button1.CustomHint := TMyCustomHint.Create(Self);
TMyCustomHint(Button1.CustomHint).FControl := Button1;
end;
end.