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

时间:2014-08-07 05:51:51

标签: delphi components runtime vcl

在设计时,我创建了一个TScrollBox,它将成为运行时创建的TLayouts的父级。 布局还包含Tlabels和Tedits,如下所示:

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;

现在我想要删除所有内容,就像从未调用此过程一样。

我尝试过以下操作,但程序会崩溃。

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

任何人都可以给我一个提示吗?

2 个答案:

答案 0 :(得分:15)

删除控件时,control list后面的控件的索引会向下移动。即,你最终试图访问不存在的位置。

您需要向下迭代列表:

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所指出的,这里有嵌套的父母。这意味着您应该从下往上释放组件。一种方法是通过递归

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

procedure freeChildControls(myControl : TControl; freeThisControl: boolean);
var
  i : integer;
  Item : TControl;
begin

  if Assigned(myControl) then
  begin
    for i := (myControl.ControlsCount - 1) downto 0 do
    begin
      Item := myControl.controls[i];
      if assigned(item) then
        freeChildControls(item, childShouldBeRemoved(item));
    end;

    if freeThisControl then
      FreeAndNil(myControl);
  end;
end;

function childShouldBeRemoved(child: TControl): boolean;
begin
  //consider whatever conditions you need
  //in my test I just checked for the child's name to be layout1 or label1
  Result := ...; 
end;

为了释放scrollbox1子控件(但不是自己),您可以这样称呼它:

freeChildControls(scrollbox1, false);

请注意,我必须添加childShouldBeRemoved函数,以避免此递归函数释放labellayout的子控件,您应该为其析构函数释放

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

答案 1 :(得分:1)

如果在运行时创建组件 - 使用父控件作为构造函数的参数。与Label1 := TLabel.Create(Layout1);一样 - 所以父母也是所有者。当你摧毁Layout1时,Label1也会被摧毁。