Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/linux/26.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Delphi-如何在运行时删除所有子组件?_Delphi_Components_Runtime_Vcl - Fatal编程技术网

Delphi-如何在运行时删除所有子组件?

Delphi-如何在运行时删除所有子组件?,delphi,components,runtime,vcl,Delphi,Components,Runtime,Vcl,在设计时,我创建了一个TScrollBox,它将是在运行时创建的tlayout的父对象。 布局还将包含TLabel和TEdit,如下所示: var Layout1: TLayout; Label1: TLabel; Edit1: TEdit; begin Layout1 := TLayout.Create(self); Layout1.Parent := ScrollBox1; Label1 := TLabel.Create(self); Label1.Parent

在设计时,我创建了一个TScrollBox,它将是在运行时创建的tlayout的父对象。 布局还将包含TLabel和TEdit,如下所示:

var
  Layout1: TLayout;
  Label1: TLabel;
  Edit1: TEdit;
begin
  Layout1 := TLayout.Create(self);
  Layout1.Parent := ScrollBox1;
  Label1 := TLabel.Create(self);
  Label1.Parent := Layout1;
  Label1.Text := 'abc';
end;
freeChildControls(scrollbox1, false);
现在我想删除所有内容,就像这个过程从未被调用过一样

我尝试了以下方法,但程序会崩溃

var
  i : integer;
  Item : TControl;
begin
  for i := 0 to Scrollbox1.ControlCount - 1 do  
  begin  
    Item := Scrollbox1.controls[i];
    Item.Free;
  end;
end;

有人能给我一个提示吗?

当您删除一个控件时,
控件列表中该控件后面的索引会向下移动。也就是说,您最终尝试访问不存在的位置

您需要向下迭代列表:

var
  i : integer;
  Item : TControl;
begin
  for i := (Scrollbox1.ControlCount - 1) downto 0 do  
  begin  
    Item := Scrollbox1.controls[i];
    Item.Free;
  end;
end;
另一种方法是始终保持索引0,释放其控件并检查是否仍有要释放的控件:

var
  i : integer;
  Item : TControl;
begin
  while Scrollbox1.ControlCount > 0 do  
  begin  
    Item := Scrollbox1.controls[0];
    Item.Free;
  end;
end;
更新

正如@DavidHeffernan所指出的,这里有嵌套的亲子关系。这意味着您应该从自下而上释放组件。一种方法是递归

基本上,您需要一个过程来封装释放子控件。代码类似于以下内容(请注意,这只是我做的一个小测试,可能需要额外的代码):

要释放
scrollbox1
子控件(但不是它本身),您可以这样称呼它:

var
  Layout1: TLayout;
  Label1: TLabel;
  Edit1: TEdit;
begin
  Layout1 := TLayout.Create(self);
  Layout1.Parent := ScrollBox1;
  Label1 := TLabel.Create(self);
  Label1.Parent := Layout1;
  Label1.Text := 'abc';
end;
freeChildControls(scrollbox1, false);
请注意,我必须添加
childShouldBeRemoved
函数,以避免使用此递归函数来释放
标签
布局
的子控件,这些控件应该留给它们的析构函数释放


实现此函数的一个可能的解决方案是使用对象列表,在其中添加创建的组件,然后在函数内部检查是否必须释放传递的子组件。

如果在运行时创建组件,则使用父控件作为构造函数的参数。Like
Label1:=TLabel.Create(Layout1)-因此父级也是所有者。当您销毁
Layout1
时,
Label1
也将被销毁。

谢谢您的回复。我刚试过,但我的程序还是崩溃了…知道为什么吗?当我运行调试器时,它显示“Project xxx.exe引发了异常类$C0000005,消息为“0x0080b8b7的访问冲突:读取地址0x00000014”。进程xxx.exe(3484)@AlvinLIn-这是试图访问nil对象上的方法的典型消息。我会添加一条debugmessage,告诉您什么控件被释放并从那里开始工作。可能被释放的控件正在对未分配的childobject进行调用。@Guillem这里有嵌套的父对象。需要递归或等效的方法。@DavidHeffernan我是很抱歉,我没有试图让你难堪。我只是想我最好能准确地指出我的问题,所以我写下了这些小片段。实际的程序可能需要一些时间来完成…但这里感谢你的帮助。看到你在这个和一些其他qs中发布的代码,你似乎做得很好gs喜欢动态创建通常完全不必要的组件,并因此遇到问题。如果您解释创建这些组件的原因,读者可能会建议其他不太容易出错的方法。例如,如果这与从数据库读取的记录有关,您看过标准TDBCt吗rlGrid,它基本上是一个滚动框,您可以使用DB感知控件填充它,这些控件会在其源数据集中的每一行复制?谢谢您的回答。是的,我确实研究了TDBCtrlGrid的详细信息,并学习了如何使用它。但是,后来我发现它不支持我正在研究的移动应用程序。啊,好的。N通常情况下,我会删除我的评论,因为它不太相关,但在这种情况下,我会留下它,以防其他读者与我有类似的想法。您的控件是允许用户更改数据还是仅用于显示?我的控件是允许用户添加/删除数据,这给我带来了很多麻烦…抱歉,我提出了所有问题当我刚接触delphi时被问到。这只是我给自己的一个小任务,学习如何编写一个与数据库交互的移动应用程序。任何回答都是非常感谢的。理解什么是
parent
,什么是
owner
parent
是显示控件的“表面”
>owner
是“owner”:-)
Label1
将被销毁(我也犯了这个错误并删除了我的答案)。
TControl.SetParent
导致
AParent.InsertControl(Self);
并且父控件将销毁它的子控件。