【问题标题】:Closing modal form(s) from outside and opening a new modal form从外部关闭模态表单并打开一个新的模态表单
【发布时间】:2016-11-16 11:25:07
【问题描述】:

在我的主表单中,我有一个按钮可以打开一个模态 Form2(它可能会打开其他模态表单)。在打开 Form2 之前,我正在设置一个计时器,它将以编程方式关闭所有活动的模态表单 (Form2.Close) 并打开一个新的模态 Form3

问题是当Form3 以模态方式打开时,Form2 仍然存在(可见),并且只有当我通过单击X 关闭Form3Form2 才会关闭。

要重现添加 3 个表单到项目添加 TButton,并在 Form1(主表单)上删除 TTimer

unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Button1: TButton;
    Timer1: TTimer;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
  private
  public
  end;

var
  Form1: TForm1;

implementation

uses Unit2, Unit3;

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
begin
  Timer1.Enabled := False;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  Timer1.Enabled := True;
  with TForm2.Create(Application) do
  try
    ShowModal;
  finally
    Free;
  end;
end;

procedure CloseActiveModalForms;
var
  I: Integer;
  F: TCustomForm;
  L: TList; // list of modal forms
begin
  L := TList.Create;
  try
    for I := 0 to Screen.CustomFormCount - 1 do
    begin
      F := Screen.CustomForms[I];
      if (fsModal in F.FormState) then
        L.Add(F);
    end;
    for I := 0 to L.Count - 1 do
      TCustomForm(L.Items[I]).Close; // this sets ModalResult := mrCancel
  finally
    L.Free;
  end;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  Timer1.Enabled := False;
  CloseActiveModalForms; // this should close TForm2 but it does not.

  with TForm3.Create(Application) do // create new Modal TForm3
  try
    ShowModal;
  finally
    Free;
  end; 
end;

end.

为什么Form2 没有关闭?为什么我调用CloseActiveModalFormsForm2 模态循环没有退出?

【问题讨论】:

    标签: delphi


    【解决方案1】:

    您的调用堆栈如下所示:

    1 Form1.Button1Click
    2 Form2.ShowModal //Local message processing loop until form closes
    3 Form1.Timer1Timer //Here you attempt to close the form
                        //but it doesn't actually until ShowModal exits
    4 Form3.ShowModal // Another message loop that doesn't return until form closes
    

    所以基本上,直到 Form3 关闭之后,您才能完成 Form2 的关闭。注意ShowModal 是一个阻塞 调用来显示一个表单。如果您只是Show Form3(即不是ShowModal),则调用不会阻塞,您会看到 Form2 能够在调用堆栈展开时关闭。


    您应该能够通过将显示 Form3 的调用延迟到 Form2 关闭之后来解决此问题。 OnFormDestroyEvent 应该足够了(不幸的是我无法测试它)。

    procedure TForm1.ShowForm3(Sender: TObject);
    var
      LForm: TForm;
    begin
      LForm := TForm3.Create(Application); //as you created it, but nil owner should suffice
      try
        LForm.ShowModal;
      finally
        LForm.Free;
      end; 
    end;
    
    procedure TForm1.Timer1Timer(Sender: TObject);
    begin
      Timer1.Enabled := False;
      //You will need to figure out how you reference the Form2 instance.
      Form2.OnFormDestroy := ShowForm3;
      CloseActiveModalForms;
      //Form2 will close after you backtrack up the call-stack.
      //When it's destroyed, your event handler will create and show a TForm3 instance.
    end;
    

    请注意,上面只是演示了这个概念。您需要根据您的最终目标来设计一种更稳健的方法。

    但是,我建议过度使用模态表单通常被认为对用户体验不友好。

    【讨论】:

    • "Form1.Timer1Timer // Here you attempt to close the form but it doesn't actually until ShowModal exits" - 这正是我的问题:为什么 Form2 不存在 ShowModal?模态循环检查ModalResult <> 0,并调用Close 设置ModalResult := mrCancel。即使我在调用CloseActiveModalForms 之后调用Application.ProcessMessages,循环也不会退出。我不知道为什么。
    • @zig。这就是模态形式的重点。他们在此过程中暂停其他形式的活动。这就是为什么您不能单击主窗体的原因——或者更具体地说,主窗体在模态窗体退出之前不会响应。 form.Close 仅​​开始关闭过程 - 但您在该过程完成之前显示另一个模式表单。因此,在您的新模式表单自行关闭之前,它无法完成。
    • 好的,我想我明白了。我没有意识到Timer1Timer 实际上是在内部 本地模式消息循环中处理的。因此,使用 PostMessage 延迟显示 Form3 的调用可以正常工作 - 这让 Form2 的本地消息循环退出,并关闭模式表单。
    • @zig - 你也可以在Close 调用之后添加TCustomForm(L.Items[I]).Hide;。模态表单的释放将被推迟,但如果有代码在 Form3 中检查它们,则会设置模态结果。但是,您的解决方案很好。这里没有风险,因为它们都是用户消息并且具有相同的优先级,但不要忘记 Close 也会发布消息 (CM_RELEASE)。
    • @SertacAkyuz,谢谢。我实际上添加了Hide(但在Close 之前)。我很确定Close 不会调用Release(默认情况下),除非您在关闭操作中明确指定caFree(请参阅TCustomForm.CloseModal)。 ShowModal;...finally Free; 正在释放模态表单。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-10-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-29
    相关资源
    最近更新 更多