29

My application is based on modal forms. Main form opens one form with ShowModal, this form opens another with ShowModal, so we have stacked modal forms. There is sometimes a problem that when we call ShowModal in new form, it hides behind previous forms, instead of showing on top. After pressing alt+tab, form comes back to the top, but this is not good solution. Did You meet this problem and how did you handle it?

EDIT:

I use Delphi 7.

LukLed
  • 30,174
  • 17
  • 80
  • 106
  • Thanks for adding the version. It helps if you include that in your question's text or tags somewhere. :-) – Ken White Oct 28 '09 at 19:07
  • I did set proper tag first, then added EDIT:) – LukLed Oct 28 '09 at 19:08
  • As long as you put it somewhere, it works. It doesn't necessarily have to be in one of the tags, as long as it's in the subject or text of the question somewhere. That lets people know what functionality you have available to you when they answer. :-) – Ken White Oct 28 '09 at 19:33
  • We have had this problem with third party app for years now and no fix has been found yet. – Paul-Sebastian Manole Dec 03 '15 at 12:22

6 Answers6

25

You didn't mention which version of Delphi...

Newer Delphi versions have added two new properties to TCustomForm: PopupMode and PopupParent. Setting PopupParent of your modal dialog to the form that's creating that dialog makes sure that the child form stays on top of it's parent. It usually fixes the problem you're describing.

I think this pair of properties were added in Delphi 2006, but it may have been 2005. They're definitely there in Delphi 2007 and up.

EDIT: After seeing you're using Delphi 7, the only suggestion I have is that, in the code that displays your modal form, you disable the form creating it, and re-enable on return. That should prevent the creating window from receiving input, which may help keep the Z-order correct.

Something like this may work (untested, as I'm no longer using D7):

procedure TForm1.ShowForm2;
begin
  Self.Enabled := False;
  try
    with TForm2.Create(nil) do
    begin
      try
        if ShowModal = mrOk then
          // Returned OK. Do something;
      finally
        Free;
      end;
    end;
  finally
    Self.Enabled := True;
  end;
end;

If Form2 creates a modal window (as you've mentioned), just repeat the process - disable Form2, create Form3 and show it modally, and re-enable Form2 when it returns. Make sure to use try..finally as I've shown, so that if something goes wrong in the modal form the creating form is always re-enabled.

LukLed
  • 30,174
  • 17
  • 80
  • 106
Ken White
  • 117,855
  • 13
  • 197
  • 405
  • 1
    Sorry, this is Delphi 7. There is no PopupMode and PopupParent, but is it good to know they exist. – LukLed Oct 28 '09 at 19:04
  • I could try this solution, but we have a lot of modal forms in project, message boxes are modal, so it may be sometimes hard to implement. But I'll try to do that where possible. – LukLed Oct 28 '09 at 21:06
  • It worked for me using Self.Enabled := False; and Self.Enabled := True; – Jordi Corbilla Apr 10 '13 at 09:27
  • I can confirm the PopupParent solution works a treat in Delphi XE2, much thanks! – Duncan Aug 30 '14 at 20:13
  • @Duncan: You can express your thanks by upvoting my answer if it was helpful to you. :-) – Ken White Aug 30 '14 at 20:38
  • Disabling the previous form doesn't help in Delphi 5, but Jim Gilmartin's answer seems to be exactly right. The problem is caused by old Delphi's setting WndParent to Application.Handle in TCustomForm.CreateParams() for the modal window. It has been explained by Borland/Inprise/CodeGear/Embarcadero/whatever since 2004 http://blog.therealoracleatdelphi.com/2004/02/popupmode-and-popupparent_10.html – Side S. Fresh Mar 29 '18 at 05:42
8

Sorry for adding a separate answer, but I have done a bit more research, and some of it indicates that my previous answer (DisableProcessWindowsGhosting) doesn't help. Since I can't always reproduce this issue, I cannot say for sure.

I found a solution that appears to appropriate. I referenced the code in Delphi 2007 for the CreateParams method and it matches pretty close (without having all of the other code that handles PopupMode).

I created the unit below which subclasses TForm.

unit uModalForms;

interface

uses Forms, Controls, Windows;
type
  TModalForm = class(TForm)
  protected
    procedure CreateParams(var params: TCreateParams); override;
  end;

implementation

procedure TModalForm.CreateParams(var params: TCreateParams);
begin
  inherited;

  params.WndParent := Screen.ActiveForm.Handle;

  if (params.WndParent <> 0) and (IsIconic(params.WndParent)
    or not IsWindowVisible(params.WndParent)
    or not IsWindowEnabled(params.WndParent)) then
    params.WndParent := 0;

  if params.WndParent = 0 then
    params.WndParent := Application.Handle;
end;

What I do then is include this unit in with a form unit, and then change the form's class (in the .pas code file) from class(TForm) to class(TModalForm)

It works for me, appears to be close to CodeGear's solution.

Jim Gilmartin
  • 773
  • 6
  • 13
  • I'll try with DisableProcessWindowsGhosting first and check if it works. Then I'll look at this. Thanks. – LukLed Nov 24 '09 at 00:51
2

From this link it appears that the problem is with the "Ghosting window" that was introduced in 2000/XP. You can disable the ghosting feature by calling the following code at startup.

procedure DisableProcessWindowsGhosting;
var
  DisableProcessWindowsGhostingProc: procedure;
begin
  DisableProcessWindowsGhostingProc := GetProcAddress(
    GetModuleHandle('user32.dll'),
    'DisableProcessWindowsGhosting');
  if Assigned(DisableProcessWindowsGhostingProc) then
    DisableProcessWindowsGhostingProc;
end; 

The only issue that I can see is that it will cause problems with the feature that allows for the user to minimize, move, or close the main window of an application that is not responding. But in this way you do not have to cover each call with the Self.Enabled := False code.

Jim Gilmartin
  • 773
  • 6
  • 13
  • If it really works, it would be great. Thank you. This problem bothers me for a very long time. What intrigues me is 'However, if the form had the WS_POPUP style and the "owner" was the correct window, even a "ghosted" form would not be allowed to switch Z-order to under it’s owner, thus there is no chance for a modal dialog to suddenly disappear.'. I didn't see any reference to Owner in Forms unit. – LukLed Nov 18 '09 at 19:28
1

Just set the Visible property of the form, that you want to open modal, to False. Then you can open it with .ShowModal(); and it will work.

0

I have found that using the "Always On Top" flag on more than one form causes problems with the Z order. And you may also find the need for the BringWindowToTop function.

When launching a message box using the built-in WinAPI (MessageBox), I have found that passing the calling window's handle is necessary in order to make sure that the the prompt appears on top all the time.

Scott W
  • 9,535
  • 2
  • 32
  • 51
  • 1
    See my response to Lars D. Owner (or Parent) have nothing to do with the problem being described. The problem is an after-effect of TAppication's hidden window being removed from the Taskbar; it caused the problem with child forms losing their proper place in the Z-order, which is what PopupParent and PopupMode were created to fix. – Ken White Oct 28 '09 at 19:05
  • Fair enough... on further inspection, I realize that I was thinking about using the Windows.MessageBox function and passing the calling window's handle to make sure that the MessageBox shows up on top of the caller -- which I was considering to be the "parent". Will edit my answer to reflect this. – Scott W Oct 28 '09 at 21:37
0

try it OnShowForm:

PostMessage(Self.Handle, WM_USER_SET_FOCUS_AT_START, 0, 0);
Mayank Jain
  • 5,476
  • 7
  • 29
  • 64
Dmitry
  • 1