Modal forms hidden by fsStayOnTop forms
Asked Answered
E

4

6

I have a form (TBigForm in the example below) which allows to manipulate some complex data and needs additional information to be shown. I put this info in a fsStayOnTop form (OnTopForm in the example) to ensure it's always visible but can be moved out of the way if necessary. Now when some user action in TBigForm shows a modal form this often gets hidden behind OnTopForm which makes the app look frozen. How can I avoid this? (Searching yields many, many hits but I wasn't able to distill a solution out of them.)

In my real app there are a lot of places where modal forms are shown, so I would like to avoid changing all of these calls.

Example: Create a new VCL application, drop a TButton on Form1, double-click the button and replace the generated Button1Click implementation stub with the following:

type
  TBigForm = class(TForm)
  strict private
    OnTopForm: TForm;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  protected
    procedure DoHide; override;
    procedure DoShow; override;
  public
    constructor Create(AOwner: TComponent); override;
  end;

{ TBigForm }

procedure TBigForm.Button1Click(Sender: TObject);
begin
  ShowMessage('Test');
end;

constructor TBigForm.Create(AOwner: TComponent);
begin
  inherited CreateNew(AOwner);

  Caption := 'Big form';
  WindowState := wsMaximized;

  Button1 := TButton.Create(Self);
  Button1.Parent := Self;
  Button1.Caption := 'Freeze!';
  Button1.SetBounds(10, 10, 100, 100);
  Button1.OnClick := Button1Click;
end;

procedure TBigForm.DoHide;
begin
  OnTopForm.Free;
  inherited DoHide;
end;

procedure TBigForm.DoShow;
begin
  inherited DoShow;
  OnTopForm := TForm.Create(Self);
  OnTopForm.Caption := 'Important information';
  OnTopForm.BorderStyle := bsToolWindow;
  OnTopForm.FormStyle := fsStayOnTop;
  OnTopForm.Position := poScreenCenter;
  OnTopForm.Show;
end;

{ TForm1 }

procedure TForm1.Button1Click(Sender: TObject);
var
  f: TBigForm;
begin
  f := TBigForm.Create(nil);
  try
    f.ShowModal;
  finally
    f.Free;
  end;
end;

Start the app, click on "Button1" and then on "Freeze!".

(BTW: We use D2007.)

Execration answered 4/8, 2010 at 18:29 Comment(3)
That's funny - after 3.5 years a downvote out of thin air, without any comment.Execration
Here's an upvote to offset the downvote.Waikiki
What is the solution for xe5?Cresa
N
2

Change temporarily the FormStyle of your OnTopform before displaying another Form as Modal:

procedure TBigForm.Button1Click(Sender: TObject);
begin
  OnTopForm.FormStyle := fsNormal;
  ShowMessage('Test');
  OnTopForm.FormStyle := fsStayOnTop;
end;

It should work for what you want...

Noahnoak answered 4/8, 2010 at 19:16 Comment(1)
Thanks! This seems to work, but it requires to manipulate each and every call to ShowModal, ShowMessage etc. - I'd like to avoid that. Using Application.OnModalBegin doesn't help here because it's only called once if you nest ShowModal calls. :-(Execration
J
2

Try setting the modal Form's PopupParent property to be the StayOnTop Form, or set the Application.ModalPopupMode property to something other than pmNone, prior to calling ShowModal().

Justis answered 4/8, 2010 at 19:30 Comment(3)
The former would change the window hierarchy of the application - the StayOnTop form would stay under the modal form. And the latter also wouldn't help since 'ShowMessage' is called from the modal form, but the StayOnTop form is a different form and it will still stay on top.Myo
You canot have it both ways. You say the problem is the modal window getting stuck behind the StayOnTop window. So obviously the modal window would need to be shown on top of it instead. Otherwise, just move the modal window to an area outside of the StayOnTop window before calling ShowModal(). As for ShowMessage(), it displays a modal TForm, and as such is subject to the TApplication.ModalPopupMode property.Justis
As I see it, the problem is not the modal form getting stuck under the StayOnTop form, it is that the ShowMessage form is getting stuck under the StayOnTop form. And since the ShowMessage is called from the Modal form and not from the StayOnTop form, it will not get owned by the StayOnTop form.. with any ModalPopupMode...Myo
C
1
procedure TForm1.ScreenOnActiveFormChange(Sender: TObject);
begin
  if (Screen.ActiveForm <> nil) then
  begin
    if (Screen.ActiveForm.Handle <> Application.MainForm.Handle) then
      with Screen.ActiveForm do
        SetWindowPos(Handle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE or SWP_NOSIZE);
    Windows.SetForeGroundWindow(Screen.ActiveForm.Handle);
  end;
end;

This should work.

Clayborn answered 5/8, 2010 at 18:15 Comment(0)
B
0

Here your goody

Create an global TApplicationEvents
Declare an global var to keep track of modal form count
Hookup the OnMessage

var
  Ctrl: TControl;

if Msg.hwnd <> 0 then
  case Msg.message of
    CM_ACTIVATE,
    CM_DEACTIVATE:
    begin
      Ctrl := FindControl(Msg.hwnd);
      if Ctrl is TForm then
        if fsModal in TForm(Ctrl).FormState then
        begin  
          if Msg.message = CM_ACTIVATE then
            Inc(Modal form count var)
          else
            Dec(Modal form count var);

          add more logic based on Modal form count var
        end;
    end;
  end;

Have fun

Belcher answered 4/8, 2010 at 21:29 Comment(1)
I can't get the Inc/Dec lines to be called - Ctrl is always nil. Have you tried this in my example app?Execration

© 2022 - 2024 — McMap. All rights reserved.