7

例如,是否可以用在运行时(有条件地)实例化的子类组件替换和释放 TEdit?如果是这样,应该如何以及何时完成?我试图将父级设置为 nil 并在表单构造函数和 AfterConstruction 方法中调用 free() 但在这两种情况下我都遇到了运行时错误。


更具体地说,我收到了访问冲突错误 (EAccessViolation)。弗朗索瓦似乎是对的,他说在框架构造中释放组件会与表单控件管理混淆。

4

3 回答 3

8

您必须调用 TEdit 父级的 RemoveControl 才能删除控件。使用 InsertControl 添加新控件。

var Edit2: TEdit;
begin
  Edit2 := TEdit.Create(self);
  Edit2.Left := Edit1.Left;
  Edit2.Top := Edit2.Top;
  Edit1.Parent.Insertcontrol(Edit2);
  TWinControl(Edit1.parent).RemoveControl(Edit1);
  Edit1.Free;
end;

将 TEdit.Create 替换为您要使用的类,并复制您需要的所有属性,就像我对 Left 和 Top 所做的那样。

于 2008-09-23T13:44:11.500 回答
8

这个更通用的例程可以与 Form 或 Frame 一起使用(更新为使用新控件的子类):

function ReplaceControlEx(AControl: TControl; const AControlClass: TControlClass; const ANewName: string; const IsFreed : Boolean = True): TControl;
begin
  if AControl = nil then
  begin
    Result := nil;
    Exit;
  end;
  Result := AControlClass.Create(AControl.Owner);
  CloneProperties(AControl, Result);// copy all properties to new control
  // Result.Left := AControl.Left;   // or copy some properties manually...
  // Result.Top := AControl.Top;
  Result.Name := ANewName;
  Result.Parent := AControl.Parent; // needed for the InsertControl & RemoveControl magic
  if IsFreed then
    FreeAndNil(AControl);
end;

function ReplaceControl(AControl: TControl; const ANewName: string; const IsFreed : Boolean = True): TControl;
begin
  if AControl = nil then
    Result := nil
  else
    Result := ReplaceControlEx(AControl, TControlClass(AControl.ClassType), ANewName, IsFreed);
end;

使用此例程将属性传递给新控件

procedure CloneProperties(const Source: TControl; const Dest: TControl);
var
  ms: TMemoryStream;
  OldName: string;
begin
  OldName := Source.Name;
  Source.Name := ''; // needed to avoid Name collision
  try
    ms := TMemoryStream.Create;
    try
      ms.WriteComponent(Source);
      ms.Position := 0;
      ms.ReadComponent(Dest);
    finally
      ms.Free;
    end;
  finally
    Source.Name := OldName;
  end;
end;

像这样使用它:

procedure TFrame1.AfterConstruction;
var
  I: Integer;
  NewEdit: TMyEdit;
begin
  inherited;
  NewEdit := ReplaceControlEx(Edit1, TMyEdit, 'Edit2') as TMyEdit;
  if Assigned(NewEdit) then
  begin
    NewEdit.Text := 'My Brand New Edit';
    NewEdit.Author := 'Myself';
  end;
  for I:=0 to ControlCount-1 do
  begin
    ShowMessage(Controls[I].Name);
  end;
end;

注意:如果您在 Frame 的 AfterConstruction 中执行此操作,请注意托管表单构建尚未完成。
在那里释放控件可能会导致很多问题,因为您正在搞乱表单控件的内务管理。
如果您尝试阅读新的 Edit Caption 以显示在 ShowMessage 中,请查看您会得到什么...
在这种情况下,您会想要使用
...ReplaceControl(Edit1, 'Edit2', False )
然后执行
...FreeAndNil (编辑1)
稍后。

于 2008-09-23T18:40:32.957 回答
1

您实际上可以使用 RTTI(查看 TypInfo 单元)来克隆所有匹配的属性。前段时间我为此编写了代码,但现在找不到了。我会继续寻找。

于 2008-09-23T18:17:24.543 回答