【问题标题】:MainFormOnTaskbar + tooltip causes focus-stealingMainFormOnTaskbar + 工具提示导致焦点窃取
【发布时间】:2012-11-29 23:43:13
【问题描述】:

我使用 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 提出一个非常好的问题

标签: delphi delphi-xe2 z-order


【解决方案1】:

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

现在,当您创建第一个 TForm2 实例时,Application.MainForm 属性仍然是 nil。并且由于您没有明确指定PopupParentTCustomForm.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;

【讨论】:

  • 谢谢。最后,我在 TForm2.CreateParams 而不是 PopupParent 中明确设置了 Params.WndParent,因为如果不设置父级就不可能创建 TForm2。我的 TForm1 实际上是隐藏的(每个 TForm2 都有自己的任务栏按钮),所以我不确定 Form1.Handle 是否一定合适。 Raymond Chen 说never use GetDesktopWindow; use 0。在测试 0 中,Form1.Handle 甚至 Application.Handle 都可以正常工作。似乎每个 TForm2 都有 same 所有者是必要的。所以我选择了 0。
  • 顺便提一下,彼得在disagrees on not using GetDesktopWindow here.
  • 如果主窗体是隐藏的,那么0 是正确的使用方式。通过WndParent 控制它也是一个不错的举措。至于GetDesktopWindow,Raymond 是正确的。彼得下面只是犯了很多人犯的同样的错误。雷蒙德描述的错误。
猜你喜欢
  • 1970-01-01
  • 2016-04-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-03
  • 2020-10-30
  • 2011-11-04
相关资源
最近更新 更多