Delphi获取(并销毁)表单

时间:2018-03-24 00:41:38

标签: forms delphi

在使用主要表单"上的TPanel来显示其他表单的应用程序中,我需要不仅能够在TPanel上显示这些表单,还需要关闭并销毁它们使用主窗体上的控件(按钮)。

目标是遵循 - 许多按钮,每个按钮在主窗体上的一个面板上显示特定的窗体。然后,一个按钮,杀死当前正在显示的任何可能的形式(=嵌入在面板上)。另外,每次打开"打开"时,应该调用相同的关闭/终止操作。按钮被触发,因此如果面板上显示某个表单,则应该用新表单替换它。

要在TPanel中显示表单,我使用以下内容:

procedure TMainForm.Button1Click(Sender: TObject);
begin
  if not assigned(form4) then
    form4:= TForm4.Create(Panel2);
  form4.Parent:= Panel2;
  form4.Show;
end;

现在,要关闭窗口,使用主窗体上的另一个按钮,我尝试了各种方法,包括CloseWindow方法,使用句柄或指针等。最有希望的方式是:

procedure TMainForm.Button2Click(Sender: TObject);
begin
  Panel2.controls[0].Free;
end;

它实际上关闭了表单,但由于未使用FreeAndNil,因此重复点击Button1会导致一系列令人讨厌的异常,因为表单(在这种情况下为form4)已被释放,但对它的引用没有,所以assigned()方法返回true,然后我尝试为某个值赋值,form4.Parent:= Panel2;不再存在。嵌入式表单上的onClose Action:= caFree也没有帮助,因为根本没有触发onClose操作...将FreeAndNil(Form4)置于Form4的onDestroy事件中,也会导致一系列异常从&#34开始;指针操作无效"。

使用

procedure TMainForm.Button2Click(Sender: TObject);
begin
  FreeAndNil(Panel2.controls[0]);
end;

导致以下错误:[dcc32 Error] Unit3.pas(47): E2197 Constant object cannot be passed as var parameter

所以获取嵌入表单实例的正确方法是什么,能够使用FreeAndNil(......表单......)?我无法通过名称来解决它,主要是因为我不知道当前显示的是什么形式。我需要能够根据事实找到一个表单实例,它属于主窗体上的TPanel,然后完全销毁它,以便再次显示Button1,再次显示它,再次单击Button2,将再次关闭并销毁它。

3 个答案:

答案 0 :(得分:5)

解决方案的关键是Vcl.Forms.TFormClass。来自文档:

  

TFormClass是TForm的元类。它的价值就是阶级   参考TForm或其后代之一。

使用如下:

首先,从定义将在主窗体面板中显示的窗体的所有单元中抛弃FormN个全局变量。你不需要它们,删除它们会阻止你犯错误。

其次,在主窗体中添加私人声明,CurrentForm: TForm和私人procedure ShowForm

...
private
  CurrentForm: TForm;
  procedure ShowForm(aFormClass: TFormClass);
...

由于aFormClass: TFormClass参数,您可以传入任何表单类型。

为应该创建和显示面板中的表单的按钮编写事件处理程序,类似于:

procedure TForm25.ShowFormAClick(Sender: TObject);
begin
  ShowForm(TForm26);
end;

并编写ShowForm()方法:

procedure TForm25.ShowForm(aFormClass: TFormClass);
begin
  CurrentForm.Free;
  CurrentForm := aFormClass.Create(self);
  CurrentForm.Parent := Panel1;
  CurrentForm.Show;
end;

最后还要编写应隐藏当前显示的任何表单的按钮的事件处理程序:

procedure TForm25.ShowNothingClick(Sender: TObject);
begin
  FreeAndNil(CurrentForm);
end;

答案 1 :(得分:1)

你做错了什么

你完全误解了Delphi的默认全局表单变量。例如。在这种情况下Form4: TForm4;。 (再一次,我感叹德尔福坚持这个可怕的设计捷径。)

仅仅因为Delphi碰巧为你生成这个并没有赋予它任何特殊意义。您 无法 假设它以任何方式绑定到TForm4的特定实例。而你肯定 不应该 假设它的Assigned状态表明存在多少个表单实例。 的确,正如您所注意到的那样,即使Assigned(Form4) = True Form4 引用的实例已经被销毁,也可能已被销毁。

为了更好地理解,请尝试以下方法:

{Add these to your main form}
FForm4a: TForm4;
FForm4b: TForm4;
FHelloWorld: TForm4;

{Try the following in a button click event
 You should see 3 instances of TForm4.}
FForm4a := TForm4(Self);
FForm4b := TForm4(Self);
FHelloWorld := TForm4(Self); {You might not have realised, but the
                              identifier can be completely different
                              to the class name.}
FForm4a.Show();
FForm4b.Show();
FHelloWorld.Show();

{In another try}
FreeAndNil(FHelloWorld);
if not Assigned(FHelloWorld) then
  ShowMessage('Code explicitly ensured the reference to the form was cleared.');
FForm4b.Free;
if Assigned(FForm4b) then
  ShowMessage('If you do not clear the reference, the form variable *remains* assigned.');

{Finally close all TForm4 instances through the UI
(if your form settings allow it), and call the following.}
if Assigned(FForm4a) then
  ShowMessage('Form4a is assigned.');
if Assigned(FForm4b) then
  ShowMessage('Form4b is assigned.');
if Assigned(FHelloWorld) then
  ShowMessage('HelloWorld is assigned.');

那你怎么解决你的问题呢?

首先,如果您想了解Panel2上的表单,请检查Panel2不要浪费时间检查 的全局变量与Panel2 上的内容有关。

function TMainForm.DoesForm4Exist(): Boolean;
begin
  Result := True;
  for I := 0 to Panel2.ControlCount-1 do
  begin
    if (Panel2.Controls[I] is TForm4) then Exit;
  end;
  Result := False;
end;

现在,您可以通过可靠的方式检查Panel2 目前是否 TForm4实例。这应该让你走上正确的道路。

改进解决方案

您可以将上述代码设为通用代码,以便将其重用于其他表单。同时,您可以返回对表单的引用,以便以编程方式与其进行交互。例如。摧毁它:

function TMainForm.FindForm(AFormClass: TFormClass): TForm;
begin
  Result := nil;
  for I := 0 to Panel2.ControlCount-1 do
  begin
    if (Panel2.Controls[I] is AFormClass) then
    begin
      Result := TForm(Panel2.Controls[I])
      Exit;
    end;
  end;
end;

{Example using the code to find and destroy a TForm4 instance}
LForm := FindForm(TForm4);
if Assigned(LForm) then LForm.Free;

您可以按照Tom's answer类似地使表单创建通用。

答案 2 :(得分:-1)

您的主要问题是,您正在尝试解决不存在的问题。主要是,您不需要跟踪可能在面板中显示的表单,因此您根本不必使用FreeAndNil

这是因为你可以随时询问专家组,当你点击按钮时,你知道你试图显示的是什么形式,你可以特别询问专家组是否有这种特定形式。

procedure DisplayFormInPanel(Panel: TPanel; FormClass: TFormClass);
begin
  if (Panel.ControlCount > 0) and not (Panel.Controls[0] is FormClass) then
    Panel.Controls[0].Free;

  if Panel.ControlCount = 0 then
  begin
    with FormClass.Create(Panel) do begin
      Parent := Panel;
      Show;
    end;
  end;
end;

上述过程摆脱了任何不想要显示的形式。如果已经存在将要显示的那种表单,则没有任何反应 - 表单保留在面板中。

称之为:

procedure TForm1.Button1Click(Sender: TObject);
begin
  DisplayFormInPanel(Panel2, TForm4);
end;

关闭任何表单的按钮点击处理程序可以像

一样简单
  if Panel.ControlCount > 0 then
    Panel.Controls[0].Free;


关于DCC误差,没有必要追求,因为控制数组中的引用与“form4”引用不同。即使你可以忽略那个引用,它也不会影响“form4”,因此无法帮助测试Assigned。一旦表单被释放,控件数组中的引用将从控件数组中删除。