在运行时删除和替换可视组件
时间:2020-03-06 14:35:50 来源:igfitidea点击:
例如,是否可以用在运行时实例化(有条件地)实例化的子类组件替换和释放TEdit?如果是这样,应该如何以及何时进行?我试图将父级设置为nil,并在表单构造函数和AfterConstruction方法中调用free(),但是在两种情况下,我都遇到了运行时错误。
更具体地说,我遇到了访问冲突错误(EAccessViolation)。 Fran?ois似乎很正确,他说在框架构造中释放组件会使Form控制内部管理混乱。
解决方案
我们必须调用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一样复制所需的所有属性。
实际上,我们可以使用RTTI(在TypInfo单元中查找)来克隆所有匹配的属性。不久前,我为此编写了代码,但现在找不到。我会继续寻找。
这个更通用的例程可以与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;
注意:如果要在框架的AfterConstruction内部进行此操作,请注意宿主Form的构建尚未完成。
在那儿释放控件可能会导致很多问题,因为我们搞砸了Form控件的内务处理。
如果我们尝试阅读要显示在ShowMessage中的新的Edit Caption,请查看得到的内容。
在这种情况下,我们可能要使用
... ReplaceControl(Edit1,'Edit2',False)
然后做一个
... FreeAndNil(Edit1)
之后。