模态窗口被fsStayOnTop窗口隐藏了。

6
我有一个表单(在下面的示例中为TBigForm),它允许操作一些复杂数据并需要显示其他信息。我将此信息放在fsStayOnTop表单中(在示例中为OnTopForm),以确保它始终可见,但如果需要,可以将其移到一边。现在,当TBigForm中的某些用户操作显示模态表单时,它经常会隐藏在OnTopForm后面,这使应用程序看起来冻结了。我该如何避免这种情况?(搜索得到了很多结果,但我无法从中提炼出解决方案。)
在我的实际应用程序中,有很多地方显示模态表单,因此我希望避免更改所有这些调用。
示例:创建一个新的VCL应用程序,在Form1上放置一个TButton,双击按钮并将生成的Button1Click实现存根替换为以下内容:
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;

启动应用程序,单击“Button1”,然后单击“Freeze!”。

(顺便说一句:我们使用的是D2007。)


很有趣 - 经过3.5年,突然出现了一个毫无根据的踩票,没有任何评论。 - Uli Gerhardt
这里有一个赞成票来抵消那个反对票。 - Shannon Matthews
@Shannon,谢谢你! :-) - Uli Gerhardt
xe5的解决方案是什么? - Nevermore
4个回答

2

在显示另一个模态窗口之前,暂时更改您的OnTopform的FormStyle:

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

它应该可以满足你的需求...


1
谢谢!这似乎能够工作,但它需要操作每个 ShowModal、ShowMessage 等函数的调用。我想避免这种情况。使用 Application.OnModalBegin 在这里没有帮助,因为如果你嵌套 ShowModal 调用,它只会被调用一次。 :-( - Uli Gerhardt

2
尝试将模态窗体的PopupParent属性设置为StayOnTop窗体,或在调用ShowModal()之前将Application.ModalPopupMode属性设置为pmNone以外的其他值。

1
前者会改变应用程序的窗口层次结构 - StayOnTop表单将保留在模态表单下方。后者也无济于事,因为'ShowMessage'是从模态表单调用的,但StayOnTop表单是一个不同的表单,它仍然会保持在顶部。 - Sertac Akyuz
你不能两全其美。你说问题在于模态窗口被卡在了StayOnTop窗口的后面。那么显然,模态窗口需要被显示在它上面。否则,在调用ShowModal()之前,只需要将模态窗口移动到StayOnTop窗口外部即可。至于ShowMessage(),它显示一个模态的TForm,因此受到TApplication.ModalPopupMode属性的影响。 - Remy Lebeau
1
在我看来,问题不是模态窗体被困在StayOnTop窗体下面,而是ShowMessage窗体被困在StayOnTop窗体下面。由于ShowMessage是从模态窗体而不是StayOnTop窗体调用的,因此它不会被任何ModalPopupMode所拥有... - Sertac Akyuz

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;

这应该可以工作。


0

这是你的好东西

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;

玩得开心


我无法调用Inc/Dec行 - Ctrl始终为nil。你在我的示例应用程序中尝试过吗? - Uli Gerhardt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接