MainFormOnTaskbar +工具提示导致焦点窃取 [英] MainFormOnTaskbar + tooltip causes focus-stealing

查看:1616
本文介绍了MainFormOnTaskbar +工具提示导致焦点窃取的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



现在,如果我将鼠标悬停在第二个,最上面的Form2上的按钮上,然后等待工具提示出现在工具提示出现的那一刻,第一个 Form2来到前面,窃取焦点。



仅当 Application.MainFormOnTaskbar True 。它还依赖于Form1的 FormCreate 方法创建的第一个Form2。如果我使用 PostMessage()来延迟第一个Form2的创建,直到应用程序完成初始化,则问题消失。


$ b $我想了解为什么会发生这种情况。我已经知道,Delphi的Application对象处理很多东西,包括提示显示,我知道Delphi可以在初始化过程中重新创建一个窗口的句柄,但是我完全不能完全解释上述的行为(或确实上述两个事实是否相关)。



Project1.dpr

 程序Project1; 

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

{$ R * .res}

begin
Application.Initialize;
Application.MainFormOnTaskbar:= True; // False使问题消失
Application.CreateForm(TForm1,Form1);
Application.Run;
结束。

Unit1.pas


$ b $单位Unit1; b

  unit Unit1; 
接口
使用
Vcl.Forms,Unit2;

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

var
Form1:TForm1;

实现
{$ R * .dfm}

程序TForm1.FormCreate(发件人:TObject);
begin
CreateForm2;
结束

程序TForm1.CreateForm2;
var
frm:TForm2;
begin
frm:= TForm2.Create(Application); //(可以通过自我 - 没有区别)
frm.Show;
结束

结束。

Unit2.pas


$ b $单位Unit2; b

  unit Unit2; 
接口
使用
Vcl.Forms,System.Classes,Vcl.Controls,Vcl.StdCtrls,WinApi.Windows;

type
TForm2 = class(TForm)
Button1:TButton; //这个按钮有一个提示
过程Button1Click(Sender:TObject);
结束

var
Form2:TForm2;

实现
使用
System.SysUtils,Unit1;

{$ R * .dfm}

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

结束。


解决方案

这里的关键问题是第一个 TForm2 被创建为由应用程序窗口 Application.Handle 拥有的窗口。在这里,我指的是所有者的Windows含义。在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 - make no difference)
frm.PopupParent:= Self;
frm.Show;
结束


I built the code below using Delphi XE2. It creates Form1, and Form1 immediately creates an instance of Form2. When I press the button on Form2 a second Form2 is created.

Now if I hover the mouse over the button on this second, topmost, Form2 and wait for the tooltip to appear, the moment the tooltip appears, the first Form2 comes to the front, stealing focus.

The problem occurs only if Application.MainFormOnTaskbar is True. It also relies on the first Form2 being created from Form1's FormCreate method. If I use PostMessage() to delay the creation of the first Form2 until the application has finished initialising, the problem goes away.

I'd like to understand why this is happening. I have already learned that Delphi's Application object handles a lot of things including hint display, and I know that Delphi can recreate a window's handle during initialisation, but I haven't been able to follow this through to explain fully the behaviour described above (or indeed whether the above two facts are even relevant).

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.

解决方案

The key issue here is that the first instance of TForm2 is created as window that is owned by the application window, Application.Handle. And here I am referring to the Windows meaning of owner. In VCL language this is known as the popup parent.

Now, when you create that first TForm2 instance, the Application.MainForm property is still nil. And because you did not explicitly assign PopupParent, the code in TCustomForm.CreateParams sets the owner to be the application window.

You simply do not want your windows to be owned by the hidden application window. This is the reason why that first TForm2 instance sometimes appears behind all the other windows, in particular behind your main form. It has simply been created with the wrong owner.

The form that is owned by Application.Handle gets shown in THintWindow.ActivateHint. That happens due to the line that reads ParentWindow := Application.Handle. This is followed by a call to SetWindowPos(Handle, ...) which results in the incorrectly owned form coming to the front. Presumably that form comes to the front because it is also owned by Application.Handle. Right now I don't have a clear explanation for the precise mechanism, but I don't find that terribly interesting because the form is clearly setup wrongly.

In any case, the fundamental problem is that you have created a window that is incorrectly owned. The solution therefore is to make sure that the window is owned correctly. Do that by assigning the PopupParent. For example:

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

这篇关于MainFormOnTaskbar +工具提示导致焦点窃取的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆