Remove and Replace a visual component at runtime

前端 未结 3 1063
隐瞒了意图╮
隐瞒了意图╮ 2020-12-31 21:17

Is it possible to, for instance, replace and free a TEdit with a subclassed component instantiated (conditionally) at runtime? If so, how and when it should be done? I\'ve t

相关标签:
3条回答
  • 2020-12-31 21:32

    You have to call RemoveControl of the TEdit's parent to remove the control. Use InsertControl to add the new control.

    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;
    

    Replace TEdit.Create to the class you want to use, and copy all properties you need like I did with Left and Top.

    0 讨论(0)
  • 2020-12-31 21:35

    You can actually use RTTI (look in the TypInfo unit) to clone all the matching properties. I wrote code for this a while back, but I can't find it now. I'll keep looking.

    0 讨论(0)
  • 2020-12-31 21:39

    This more generic routine works either with a Form or Frame (updated to use a subclass for the new control):

    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;
    

    using this routine to pass the properties to the new control

    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;
    

    use it like:

    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;
    

    CAUTION: If you are doing this inside the AfterConstruction of the Frame, beware that the hosting Form construction is not finished yet.
    Freeing Controls there, might cause a lot of problems as you're messing up with Form controls housekeeping.
    See what you get if you try to read the new Edit Caption to display in the ShowMessage...
    In that case you would want to use
    ...ReplaceControl(Edit1, 'Edit2', False)
    and then do a
    ...FreeAndNil(Edit1)
    later.

    0 讨论(0)
提交回复
热议问题