6

My application is based of a MainForm, DetailForms and DialogForms. On the taskbar I can see the MainFormButton and also the DetailForms. Therefore I use:

procedure <DetailForm>.CreateParams(var Params: TCreateParams);
begin
  inherited CreateParams(Params);
  Params.ExStyle := Params.ExStyle or WS_EX_APPWINDOW;
  Params.WndParent:= GetDesktopWindow;  
end; 

I use delphi 2010 and I've set Application.MainFormOnTaskbar:= True; When I use PromptForFileName or TSaveDialog in the Detailform then the DetailForm go behind the Mainform. The DetailForm come back after closing the dialog.

When I use DialogForm (Showmodal of TForm with property PopupMode: pmAuto) then my DetailForm is stay between the main and dialog. How can I force the TSaveDialog like a showmodal with property PopupMode: pmAuto or how can i prevent that my detailform goes behind the mainform

Demo:

program Project1;

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

  {$R *.res}

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

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ImgList, ActnList;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

uses Unit2;

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
  oForm: TForm;
begin
  oForm:= Unit2.TForm2.Create(Self);
  oForm.Show;
end;
end.

unit Unit2;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm2 = class(TForm)

    SaveDialog1: TSaveDialog;
    procedure cxButton1Click(Sender: TObject);
  private
  protected
    procedure CreateParams(var Params: TCreateParams); override;
        { Private declarations }

  public
    { Public declarations }
  end;

var
  Form2: TForm2;

implementation

{$R *.dfm}

{ TForm2 }

procedure TForm2.CreateParams(var Params: TCreateParams);
begin
  inherited CreateParams(Params);
  Params.ExStyle := Params.ExStyle or WS_EX_APPWINDOW;
  Params.WndParent:= 0;   // --> Testing
end;

procedure TForm2.cxButton1Click(Sender: TObject);
begin
  self.SaveDialog1.execute();
end;

end.
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
Ravaut123
  • 2,764
  • 31
  • 46

1 Answers1

12

Step 1 is that you must not make the desktop window the owner of your form. Raymond Chen explains why not.

To really understand what's happening you need to read Window Features on MSDN to get a clearer understanding of window ownership. And be very careful that window ownership is a concept completely unrelated to Delphi component ownership. In Delphi terms, window ownership is controlled by the PopupParent property.

As has been clarified in comments, you want both forms to be unowned, top-level windows. The main form automatically is that. For the details form you need to set WndParent to 0 and that's it:

procedure <DetailForm>.CreateParams(var Params: TCreateParams);
begin
  inherited;
  Params.WndParent := 0;
end; 

The final step is to make sure that the save dialog is owned properly. To do that specify the owner when you call Execute:

Self.SaveDialog1.Execute(Self.Handle);

So, in summary you need to make three changes:

  1. Set the detail form's WndParent to 0.
  2. Remove the WS_EX_APPWINDOW extended style, it is not needed for an unowned top-level window.
  3. Pass the detail form's handle when calling Execute on the save dialog.

Update

It turns out that you are using XP, and the Delphi code that shows the file dialog is rubbish. Although you pass a handle to the Execute method, that is ignored and the main window handle is used as the dialog's owner. And that's why the main window comes to the front.

You can get around this by setting Application.ModalPopupMode to pmAuto. You should probably set this in your .dpr file.

Read more about this here: https://web.archive.org/web/20140806033012/https://blogs.embarcadero.com/abauer/2005/09/30/21517 (on archive.org because the original page does no longer exist)

dummzeuch
  • 10,975
  • 4
  • 51
  • 158
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • No, doesn't work. I've change the wndparent = 0, also skip WS_EX_APPWINDOW and made main owner of detail. Detail go behind the main – Ravaut123 Jan 24 '13 at 13:58
  • 1
    If you set `WndParent` to `0` then you have an unowned window. Setting `WndParent` is how you control window ownership. Remember that window ownership is completely different from Delphi's component ownership. In Delphi the window ownership is controlled by the `PopupParent` property. I suspect you did that. Use the code from the answer. You want a window that is owned by the main form. Use `PopupParent` to make that happen. And then to get your window on the taskbar, add `WS_EX_APPWINDOW`. – David Heffernan Jan 24 '13 at 13:59
  • I must also can click on the main and the main form go to the top. When I click on the taskbar to the detailform then the detail comes to the top. – Ravaut123 Jan 24 '13 at 14:08
  • You have conflicting demands here. First of all you complain that the details form goes behind the main form. The solution to that is to make the details form be owned by the main form. Now you say you want the main form to go on top of the details form. Well, you can't have it both ways. What do you really want?! Now that you have read the MSDN topic that I linked to you need to decide what ownership you want for your windows. – David Heffernan Jan 24 '13 at 14:12
  • When I call a TSaveDialog on the detailform, the detailform go behind the mainform. When I have only the main and some detailforms then is it possible to click which form i want , also the main form. – Ravaut123 Jan 24 '13 at 14:17
  • OK, I think I know what you want. My latest update covers it I believe. – David Heffernan Jan 24 '13 at 14:25
  • Sorry, but it doesn't work. I've change the demo like the lastest update but the detail form go behind the main. – Ravaut123 Jan 24 '13 at 14:39
  • Is it exact the same code like the demo? Also Application.MainFormOnTaskbar := True; – Ravaut123 Jan 24 '13 at 14:42
  • Yes, I took your demo exactly, copy/paste, and applied the changes. – David Heffernan Jan 24 '13 at 14:42
  • What are the popupmode of the forms. My forms is this pmNone – Ravaut123 Jan 24 '13 at 14:43
  • Yep, same for me. I've got default .dfm files. – David Heffernan Jan 24 '13 at 14:46
  • I remake the demo and get back the detailform behind the main form. I use Delphi2010 also the forms don't have a popupparent. – Ravaut123 Jan 24 '13 at 14:57
  • Start with the code in the question, and apply my changes. Then you will be fine. – David Heffernan Jan 24 '13 at 14:58
  • The changes are applied in the code of the demo question but It doesn't work . Do I forgot something. – Ravaut123 Jan 24 '13 at 15:03
  • I disable the runtime themes in the options but don't work. I work on windows XP – Ravaut123 Jan 24 '13 at 15:11
  • 1
    No, don't disable runtime themes! I asked a question. Please just answer the question! Which save dialog is showing? The new Vista one, or the rubbish legacy XP version? The legacy version doesn't set the dialog owner properly which could be the explanation. – David Heffernan Jan 24 '13 at 15:13
  • The old one I think. How can I set the Vista one? – Ravaut123 Jan 24 '13 at 15:15
  • Ok, this to the job in the demo. I test this on other application for xp and W7 – Ravaut123 Jan 24 '13 at 15:27
  • Thanks David for the clarification. I also skip the 'Params.WndParent := 0;' when the detailforms is showing into a panel. There I get an error about 'no parentwindow', but now solved. – Ravaut123 Jan 25 '13 at 10:33
  • Well, naturally, you only ever set `WndParent` to `0` for top-level windows! Thanks for the accept. And thanks for teaching me about `ModalPopupMode`. – David Heffernan Jan 25 '13 at 10:35