在我的主要形式中,我有一个打开模态的按钮Form2(可以打开其他模态形式).在打开之前Form2我正在设置一个计时器,它将以编程方式关闭所有活动的模态形式(Form2.Close)并打开一个新的模态Form3.
问题是,当Form3以模态方式打开时,Form2保持(可见)并且仅当我Form3通过单击X将Form2关闭时关闭.
要重现3组的形式添加到项目中添加TButton和删除一个TTimer上Form1(主要形式):
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.
Run Code Online (Sandbox Code Playgroud)
为什么Form2不关门?为什么Form2我打电话后模态循环没有退出CloseActiveModalForms?
您的调用堆栈如下所示:
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
Run Code Online (Sandbox Code Playgroud)
所以基本上,在Form3关闭之前,你无法完成Form2的关闭.请注意,这ShowModal是一个阻止调用以显示表单.如果您只是ShowForm3(即没有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;
Run Code Online (Sandbox Code Playgroud)
注意上面简单地说明了这个概念.您需要根据您的最终目标设计更强大的方法.
但是,我建议过度使用模态表单通常被认为在用户体验方面不友好.