views:

163

answers:

2

Showing 2 secondary forms from the main form and then closing both forms will cause the main form to lose focus. (another application gets activated instead of mine)

The secondary forms are created either by the Main Form directly or by creating the third form from the second form.

The secondary forms set caFree in the OnClose event:

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := caFree;
end;

Using Delphi 2009 (Update 3 & 4) with XP SP3.

Here are my steps for reproducing the problem:

  1. Create a new VCL forms applications
  2. Assign the OnClose event as above
  3. Drag a button onto the created form
  4. In the click handler create a new TForm1 and show it as below

Run the program. Click the button to show a second form. Click the button on the second form to create a third form. When closing both new forms the main form will lose its focus.

This is my code in the button click event handler:

with TForm1.Create(Application) do
    show;

Is there any way to stop my main form from losing focus?

(Interestingly, when creating both secondary forms directly from the Main Form, the issue will only appear when closing the first created form then the second created form)


In the past I had the same issue which was solved by updating my delphi installation, but in that scenario I didn't use caFree in the OnClose event which is the cause for this bug.

A recommendation to set the Parent property on the secondary forms to Main Form, makes the new forms bounded to the Main Form which I'd rather not have. (and the solution proposed there to always reactivate the Main Form causes the activation order of the forms to be lost)

+1  A: 

A quick and dirty solution is to comment MainFormOnTaskbar line in the project source:

program Project1;

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

{$R *.res}

begin
  Application.Initialize;
//  Application.MainFormOnTaskbar := True;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.

Updated

If you want the MainForm to be always behind the other forms you should also override CreateParams. The following code works as you expect, though I suspect it may appear unusable for some other reason:

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  protected
    procedure CreateParams(var Params: TCreateParams); override;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
  with TForm1.Create(Application) do
    show;
end;

procedure TForm1.CreateParams(var Params: TCreateParams);
begin
  inherited CreateParams(Params);
  if Application.MainForm <> nil
    then Params.WndParent:= Application.MainForm.Handle
    else Params.WndParent:= 0;
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := caFree;
end;

end.
Serg
Already tried and unusable, this will cause all secondary forms to be able to go to the back of Main Form.
Y Low
Implementing your updated code shows two forms on the taskbar right when the program starts.
Y Low
A: 

To prevent the mainform losing focus, you need to comment out the

// Application.MainFormOnTaskBar := True;

as @Serg already suggested. The drawback of this, as you already noticed, is that the secondary forms can go behind the mainform. This is easily prevented by setting the form's PopupMode to pmAuto, which ensure that forms created by a form will stay on top of the form from which they were created.

However, this also ensures that forms created from a secondary form will be closed when the form that created them is closed. For example:

  • MainForm creates Secondary1
  • Secondary1 creates Secondary2 and Secondary3

Closing Secondary1 will close Secondary2 and Secondary3 as well.

If that is unwanted behaviour, you can assert more control by setting the PopupParent explicitely. For example to "parent" all forms to the application's main form:

procedure TForm1.FormCreate(Sender: TObject);
begin
  PopupMode := pmAuto;
  if Self <> Application.MainForm then
    PopupParent := Application.MainForm;
end;

This ensures that the Application.MainForm will remain behind all other forms; all other forms can switch to the foreground; and all forms will close when the mainform is closed.

Marjan Venema
Setting PopupMode to pmAuto brings back the original issue of losing focus
Y Low
Yes, that is why you also need the MainFormOnTaskBar commented out or set to false. It only works if you do both.
Marjan Venema
@Marjan: He means that if you do `PopupMode := pmAuto` the focus problem will be there *even if* `MainFormOnTaskBar := false`.
Andreas Rejbrand
@Andreas: hmm, it solved it for me, but going back into D2009 to check, it is now indeed showing the problem again. Darn...
Marjan Venema
@Andreas, @Y Low: D'oh, if you activate the main form to see whether it stays behind the secondary forms and then only click on the close crosses of those secondary forms, these do not get focus and the main form remains active. Fooled by that. Going back to investigation...
Marjan Venema