How to show a modal form on top of its owner form (its owner is set to fsStayOnTop or not), just as TOpenDialog does
Asked Answered
E

1

9

Summarization:

Please see the helpful comments below from Craig and Sertac.

======================================================

As shown in the following minimized code, TForm10 is set to be fsStayOnTop. TForm10.btnTryDlgClick call dlgOpen1.Execute, and the dialog shown is as expected. However, when I call TForm11.Create(Self).ShowModal inside TForm10.btnTryFormClick, the form is hidden behind the TForm10. I am wondering how to understand this behavior, and why standard TOpenDialog can show as expected? Any comment is appreciated!

PS: One workaround is to override the CreateParams procedure of TForm11, and set Params.wndParent to 0. But it looks to me that window hierarchy will be broke using this workaround.

  procedure TForm11.CreateParams(var Params: TCreateParams); // override;
  begin
    inherited;
    params.wndParent := 0;
  end;

PS: Another workaround is mentioned by Remy in the below relevant SO pages: setting the modal Form's PopupParent property to be the StayOnTop Form. But in the followed comments, Sertac mentioned this workaround will also break window hierarchy.

PS: Possibly relevant SO pages:
Modal forms hidden by fsStayOnTop forms
How Can I Keep the FindDialog from Staying on Top (Delphi)?
How to make sure a dialog is always front of the main window
Form is hidden behind other forms when ShowModal is called
Make 2 forms able to overlap each other?
Multiple form Delphi applications and dialogs
Newly created modal window loses focus and become inacessible in Windows Vista
Delphi - How to prevent Forms/MsgBoxes to move under prior form?
How to allow Delphi secondary forms behind the main form
Fake modal dialog using Show?
Delphi MainFormOnTaskBar Modal windows bug

Source for Unit10:

    unit Unit10;

    interface

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

    type
      TForm10 = class(TForm)
        btnTryDlg: TButton;
        dlgOpen1: TOpenDialog;
        btnTryForm: TButton;
        procedure FormCreate(Sender: TObject);
        procedure btnTryDlgClick(Sender: TObject);
        procedure btnTryFormClick(Sender: TObject);
      end;

    var
      Form10: TForm10;

    implementation

    {$R *.dfm}

    uses
      Unit11;

    procedure TForm10.FormCreate(Sender: TObject);
    begin
      FormStyle := fsStayOnTop;
    end;

    procedure TForm10.btnTryDlgClick(Sender: TObject);
    begin
      dlgOpen1.Execute;
    //  dlgOpen1.Execute(Self.Handle);
    end;

    procedure TForm10.btnTryFormClick(Sender: TObject);
    begin
      TForm11.Create(Self).ShowModal;
    end;

    end.

DFM for Unit10:

    object Form10: TForm10
      Left = 0
      Top = 0
      Caption = 'Form10'
      ClientHeight = 255
      ClientWidth = 414
      Color = clBtnFace
      Font.Charset = DEFAULT_CHARSET
      Font.Color = clWindowText
      Font.Height = -11
      Font.Name = 'Tahoma'
      Font.Style = []
      OldCreateOrder = False
      OnCreate = FormCreate
      PixelsPerInch = 96
      TextHeight = 13
      object btnTryDlg: TButton
        Left = 32
        Top = 24
        Width = 153
        Height = 201
        Caption = 'Try dialog'
        TabOrder = 0
        OnClick = btnTryDlgClick
      end
      object btnTryForm: TButton
        Left = 224
        Top = 24
        Width = 153
        Height = 201
        Caption = 'btnTryForm'
        TabOrder = 1
        OnClick = btnTryFormClick
      end
      object dlgOpen1: TOpenDialog
        Left = 96
        Top = 168
      end
    end

Source for Unit11:

    unit Unit11;

    interface

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

    type
      TForm11 = class(TForm)
      end;


    implementation

    {$R *.dfm}

    end.

DFM for Unit11:

    object Form11: TForm11
      Left = 0
      Top = 0
      Caption = 'Form11'
      ClientHeight = 183
      ClientWidth = 203
      Color = clBtnFace
      Font.Charset = DEFAULT_CHARSET
      Font.Color = clWindowText
      Font.Height = -11
      Font.Name = 'Tahoma'
      Font.Style = []
      OldCreateOrder = False
      PixelsPerInch = 96
      TextHeight = 13
    end
Embroideress answered 22/3, 2011 at 18:23 Comment(4)
Xichen - The question you refer is different in that a modal form is launched from an fsNormal form while there is another fsStayOnTop form in the application. In that question if you set PopupParent of the modal form to be the on-top form it will clearly be owned by the on-top form, but it should be owned by the form which it was launched from as per the application design. That was what I meant by breaking the hierarchy.Molluscoid
BTW, I copy pasted your code and cannot duplicate the problem, the modal form is above Form10 when I click btnTryForm. +1 for the link collection (research) though..Molluscoid
@Sertac: Thank you very much for your time and helpful comments about window hierarchy! Regarding the current question, I am sorry that it cannot be reproduced. "It works on my machine..." O_OEmbroideress
+1 from me for the research effort, too.Harlow
H
5

Set the modal form's PopupParent property, exactly like Remy suggested. That will parent the dialog to the StayOnTop form, which is what the dialog's Execute method is already doing. I'm not sure where Sertac's comments are coming from, but using PopupParent sets the window heirarchy correctly, so the dialog will always be above the StayOnTop form.

Hutchins answered 22/3, 2011 at 18:37 Comment(11)
@Craig: Thank you very much for your suggestion! Do you mean that it is best practice to use PopupParent in such circumstances? Could you help to confirm? Could you also help to comment how to judge in practice whether the window hierarchy is preserved correctly or not?Embroideress
@Craig: Furthermore, if using PopupParent is the best practice in such circumstances, could you help to comment whether standard dialogs, such as the TOpenDialog mentioned in the beginning of the question, also use PopupParent to achieve the functionality? Or they have their own ways?Embroideress
If you want a specific dialog to always appear above a specific form then yes, set the PopupParent property. That's what it's there for. You can usually just set Application.ModalPopupMode to pmAuto and Delphi will handle everything for you. When that's set the dialog will be parented to the currently active form (usually Application.MainForm), rather than Application.Handle, which keeps it from getting stuck behind the main form. The case where that won't work is when you have an fsStayOnTop form that isn't active, in which case the dialog will appear behind it.Cartilage
The standard dialogs depend on the Application.ModalPopupMode to get their parent. You can see the details in Dialogs.pas::TCommonDialog.Execute. It's a very similar idea though. The details for other dialogs are handled by Forms.pas::TCustomForm.CreateParams.Cartilage
Short answer: Set Application.ModalPopupMode to pmAuto and don't worry about it. The only time you'll see problems is you have a normal form and a stay-on-top form open at the same time and you open a dialog while normal dialog has focus. If that occurs and the dialog should appear on top of the fsStayOnTop form too set PopupParent to the fsStayOnTop form.Cartilage
@Craig - I put a comment on the question with a link in case anyone wonders where the comments are coming from. It is the same case in your first comment, the modal form is launched from a normal form while there is another stay-on-top form.Molluscoid
@Craig: Thank you very much for your detailed and knowledgeable suggestions! Because I have FileBox eXtender from hyperionics installed on my machine, I do have a fsStayOnTop main form from time to time. Thus, the PopupParent is the best choice for me!Embroideress
@CraigPeterson: Came across the same issue and I looked at your suggestion to set the Application.ModalPopupMode to pmAuto. It seems to work. To test it I created a test project (dl.dropboxusercontent.com/u/35370420/TestModalDialogs.zip) and adding Application.ModalPopupMode := pmAuto; to the dproj file corrects the issue even when the PopupParent is not set. In one of your comments you mentioned that this doesn't always work. Would you be kind enough to provide a sample where this wouldn't work? It's not very clear for me...Thank you!Discreet
@CraigPeterson: To reproduce the issue Xichen Li was having with my app follow these steps: 1. Run the app and click on the Show Form button. Form 1 is displayed. 2.Check ‘Stay On Top’ and click the Create Form button. Form 2 is displayed. 3. Click the Create Form button on Form 2 (leave the checkboxes unchecked). Form 3 is displayed behind Form 2. You can close it by pressing Alt+F4, if you cannot access its title bar.Discreet
@ZoëPeterson. Your tip work perfect. Application.ModalPopupMode = pmAuto. Perfect friend. Thanks.Sepulture
IMHO it would be essential to mention that this solution works only on new Delphi versions (from which one). There is no Application.ModalPopupMode property in Delphi7.Tina

© 2022 - 2024 — McMap. All rights reserved.