MainFormOnTaskbar +工具提示导致焦点窃取

时间:2012-12-12 09:43:23

标签: delphi delphi-xe2 z-order

我使用Delphi XE2构建了下面的代码。它创建Form1,Form1立即创建Form2的实例。当我按下Form2上的按钮时,会创建第二个Form2。

现在如果我将鼠标悬停在第二个最顶层的Form2上,等待工具提示出现,工具提示出现的那一刻,第一个 Form2出现在前面,偷走了焦点

仅当Application.MainFormOnTaskbarTrue时才会出现此问题。它还依赖于从Form1的FormCreate方法创建的第一个Form2。如果我使用PostMessage()来延迟创建第一个Form2,直到应用程序完成初始化,问题就会消失。

我想了解为什么会这样。我已经知道Delphi的Application对象处理很多事情,包括提示显示,我知道Delphi可以在初始化期间重新创建一个窗口的句柄,但是我无法通过这个来完全解释上述行为(或者事实上,上述两个事实是否相关)。

Project1.dpr

program Project1;

uses
  Vcl.Forms,
  Unit1 in 'Unit1.pas' {Form1},
  Unit2 in 'Unit2.pas' {Form2};

{$R *.res}

begin
  Application.Initialize;
  Application.MainFormOnTaskbar := True; // False makes problem go away
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.

Unit1.pas

unit Unit1;
interface
uses
  Vcl.Forms, Unit2;

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
  public
    procedure CreateForm2;
  end;

var
  Form1: TForm1;

implementation
{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  CreateForm2;
end;

procedure TForm1.CreateForm2;
var
  frm : TForm2;
begin
  frm := TForm2.Create(Application); // (Could pass Self - makes no difference)
  frm.Show;
end;

end.

Unit2.pas

unit Unit2;
interface
uses
  Vcl.Forms, System.Classes, Vcl.Controls, Vcl.StdCtrls, WinApi.Windows;

type
  TForm2 = class(TForm)
    Button1: TButton; // This button has a hint
    procedure Button1Click(Sender: TObject);
  end;

var
  Form2: TForm2;

implementation
uses
  System.SysUtils, Unit1;

{$R *.dfm}

procedure TForm2.Button1Click(Sender: TObject);
begin
  Form1.CreateForm2;
end;

end.

1 个答案:

答案 0 :(得分:7)

这里的关键问题是TForm2的第一个实例被创建为应用程序窗口Application.Handle所拥有的窗口。在这里,我指的是Windows meaning of owner。在VCL语言中,这称为弹出父级。

现在,当您创建第一个TForm2实例时,Application.MainForm属性仍为nil。由于您未明确指定PopupParent,因此TCustomForm.CreateParams中的代码会将所有者设置为应用程序窗口。

您根本不希望您的窗口被隐藏的应用程序窗口拥有。这就是为什么第一个TForm2实例有时出现在所有其他窗口后面的原因,特别是在主窗体后面。它只是由错误的所有者创建的。

Application.Handle所拥有的表单会显示在THintWindow.ActivateHint中。这是因为读取ParentWindow := Application.Handle的行。接下来是对SetWindowPos(Handle, ...)的调用,导致错误拥有的表单出现在前面。据推测,这种形式出现在前面,因为它也归Application.Handle所有。现在我对准确的机制没有明确的解释,但是我发现这并不是很有趣,因为表格设置错误。

在任何情况下,根本问题在于您创建了一个错误拥有的窗口。因此,解决方案是确保窗口正确拥有。通过分配PopupParent来做到这一点。例如:

procedure TForm1.CreateForm2;
var
  frm : TForm2;
begin
  frm := TForm2.Create(Application); // (Could pass Self - makes no difference)
  frm.PopupParent := Self;
  frm.Show;
end;