代码之家  ›  专栏  ›  技术社区  ›  user16120

在运行时移除并替换可视化组件

  •  7
  • user16120  · 技术社区  · 16 年前

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


    更具体地说,我得到了一个访问冲突错误(eacessviolation)。Fran_ois说框架结构中的部件释放会干扰表单控制的内务管理,这似乎是对的。

    3 回复  |  直到 12 年前
        1
  •  8
  •   Francesca    16 年前

    这个更通用的例程可以与窗体或框架一起工作(更新后为使用新控件的子类):

    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;
    

    注意安全 :如果在框架的后期构造中执行此操作,请注意宿主窗体构造尚未完成。
    释放那里的控件,可能会导致许多问题,因为您正在处理表单控件的内务管理。
    如果您尝试阅读要在ShowMessage中显示的新编辑标题,请查看获得的内容…
    在这种情况下你会想用
    …替换控制(edit1,'edit2', )
    然后做一个
    …免费(第1版)
    后来。

        2
  •  8
  •   Loesje    16 年前

    必须调用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。创建到您要使用的类,并复制您需要的所有属性,就像我对左上角所做的那样。

        3
  •  1
  •   Jim McKeeth    16 年前

    实际上,可以使用rtti(在typinfo单元中查找)克隆所有匹配的属性。我为这个写了一段时间的代码,但现在找不到了。我会继续找的。