在使用主要表单"上的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,将再次关闭并销毁它。
答案 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
。一旦表单被释放,控件数组中的引用将从控件数组中删除。