例如,是否可以在运行时使用实例化(有条件地)的子类组件替换和释放TEdit?如果是这样,应该如何以及何时完成?我试图将父设置为nil并在表单构造函数和AfterConstruction方法中调用free(),但在这两种情况下我都遇到了运行时错误.
更具体地说,我遇到了访问冲突错误(EAccessViolation).看起来弗朗索瓦是正确的,因为他说在框架构造中释放组件会使用Form来控制内务管理.
解决方法
这个更通用的例程可以使用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控件的内务管理.如果您尝试阅读ShowMessage中显示的新编辑标题,请查看您获得的内容…在那种情况下,你会想要使用… ReplaceControl(Edit1,’Edit2′,False)然后做一个… FreeAndNil(EDIT1)后来.