Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MainFormOnTaskbar + tooltip causes focus-stealing

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.
like image 245
Ian Goldby Avatar asked Dec 12 '12 09:12

Ian Goldby


1 Answers

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;
like image 150
David Heffernan Avatar answered Nov 06 '22 04:11

David Heffernan