2012-12-12 32 views
4

我使用Delphi XE2构建了以下代码。它创建Form1,Form1立即创建Form2的一个实例。当我按下Form2上的按钮时,会创建第二个Form2。MainFormOnTaskbar +工具提示导致焦点窃取

现在,如果我将鼠标悬停在第二个顶部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. 
+0

+1对于一个很好的问题 –

回答

6

的关键问题是, 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; 
+0

谢谢。最后,我在TForm2.CreateParams中明确地设置了Params.WndParent而不是PopupParent,因为如果不设置父级,就不可能创建TForm2。我的TForm1实际上是隐藏的(每个TForm2都有自己的Taskbar按钮),所以我不确定Form1.Handle是否合适。 Raymond Chen说[从来不使用GetDesktopWindow;使用0](http://blogs.msdn.com/b/oldnewthing/archive/2004/02/24/79212.aspx)。在测试0中,Form1.Handle甚至Application.Handle都工作。似乎有必要每个TForm2拥有_same_所有者。所以我解决了0. –

+0

顺便说一句,彼得下面[不同意在这里不使用GetDesktopWindow](https://groups.google.com/forum/?hl=zh-CN&fromgroups=#!msg/borland.public.delphi.winapi/7mNoFJpYTGQ/62DpjPRf9-千焦)。 –

+1

如果主表单被隐藏,那么使用'0'是正确的。通过WndParent控制它也是一个很好的举动。至于'GetDesktopWindow',雷蒙德是正确的。 Peter Below正在犯这么多人犯的同样的错误。雷蒙德描述的错误。 –