【问题标题】:Duplicating components at Run-Time在运行时复制组件
【发布时间】:2008-10-27 03:23:51
【问题描述】:

有没有一种简单的方法可以复制父组件下的所有子组件,包括它们发布的属性?

例如:

  • TP 面板
    • 标签
    • TEdit
    • TListView
    • TSpecialClassX

当然最重要的因素是,它应该复制我在TPanel上放置的任何新组件,而不用在正常情况下修改代码。

我听说过 RTTI,但实际上从未使用过。有什么想法吗?

【问题讨论】:

    标签: delphi rtti


    【解决方案1】:

    在通过父控件循环创建 dup 组件后,您可以适当地使用 CLoneProperties routine from the answer 到“Replace visual component at runtime”。

    更新:一些工作代码......

    。我从您的问题中假设您想要复制 WinControl 中包含的控件(因为 Parent 是 TWinControl)。
    .由于我不知道您是否还想使用与原始控件相同的事件处理程序来挂钩重复的控件,所以我为此做了一个选项。
    .您可能希望为重复的控件提供一个适当的有意义的名称。

    uses
      TypInfo;
    
    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 CloneEvents(Source, Dest: TControl);
    var
      I: Integer;
      PropList: TPropList;
    begin
      for I := 0 to GetPropList(Source.ClassInfo, [tkMethod], @PropList) - 1 do
        SetMethodProp(Dest, PropList[I], GetMethodProp(Source, PropList[I]));
    end;
    
    procedure DuplicateChildren(const ParentSource: TWinControl;
      const WithEvents: Boolean = True);
    var
      I: Integer;
      CurrentControl, ClonedControl: TControl;
    begin
      for I := ParentSource.ControlCount - 1 downto 0 do
      begin
        CurrentControl := ParentSource.Controls[I];
        ClonedControl := TControlClass(CurrentControl.ClassType).Create(CurrentControl.Owner);
        ClonedControl.Parent := ParentSource;
        CloneProperties(CurrentControl, ClonedControl);
        ClonedControl.Name := CurrentControl.Name + '_';
        if WithEvents then
          CloneEvents(CurrentControl, ClonedControl);
      end;
    end;
    
    procedure TForm1.Button1Click(Sender: TObject);
    begin
      DuplicateChildren(Panel1);
    end;
    

    【讨论】:

    • 这个解决方案虽然不支持多级子控件(例如:TPanel 包含一个包含任何内容的TPanel)
    • 我做错了什么或者这不会复制例如滑块编辑框连接?因为我的滑块在复制后停止控制编辑框
    【解决方案2】:

    阅读本页

    Run-Time Type Information In Delphi - Can It Do Anything For You?

    注意Copying Properties From A Component To Another部分

    它有一个单元,RTTIUnit 和一个过程,它似乎做了你想要的一部分,但我认为它不会复制任何没有额外代码的子组件。 (我认为可以将其粘贴在这里...)

    procedure CopyObject(ObjFrom, ObjTo: TObject);    
      var
    PropInfos: PPropList;
    PropInfo: PPropInfo;
    Count, Loop: Integer;
    OrdVal: Longint;
    StrVal: String;
    FloatVal: Extended;  
    MethodVal: TMethod;
    begin
    //{ Iterate thru all published fields and properties of source }
    //{ copying them to target }
    
    //{ Find out how many properties we'll be considering }
    Count := GetPropList(ObjFrom.ClassInfo, tkAny, nil);
    //{ Allocate memory to hold their RTTI data }
    GetMem(PropInfos, Count * SizeOf(PPropInfo));
    try
    //{ Get hold of the property list in our new buffer }
    GetPropList(ObjFrom.ClassInfo, tkAny, PropInfos);
    //{ Loop through all the selected properties }
    for Loop := 0 to Count - 1 do
    begin
      PropInfo := GetPropInfo(ObjTo.ClassInfo, PropInfos^[Loop]^.Name);
     // { Check the general type of the property }
      //{ and read/write it in an appropriate way }
      case PropInfos^[Loop]^.PropType^.Kind of
        tkInteger, tkChar, tkEnumeration,
        tkSet, tkClass{$ifdef Win32}, tkWChar{$endif}:
        begin
          OrdVal := GetOrdProp(ObjFrom, PropInfos^[Loop]);
          if Assigned(PropInfo) then
            SetOrdProp(ObjTo, PropInfo, OrdVal);
        end;
        tkFloat:
        begin
          FloatVal := GetFloatProp(ObjFrom, PropInfos^[Loop]);
          if Assigned(PropInfo) then
            SetFloatProp(ObjTo, PropInfo, FloatVal);
        end;
        {$ifndef DelphiLessThan3}
        tkWString,
        {$endif}
        {$ifdef Win32}
        tkLString,
        {$endif}
        tkString:
        begin
          { Avoid copying 'Name' - components must have unique names }
          if UpperCase(PropInfos^[Loop]^.Name) = 'NAME' then
            Continue;
          StrVal := GetStrProp(ObjFrom, PropInfos^[Loop]);
          if Assigned(PropInfo) then
            SetStrProp(ObjTo, PropInfo, StrVal);
        end;
        tkMethod:
        begin
          MethodVal := GetMethodProp(ObjFrom, PropInfos^[Loop]);
          if Assigned(PropInfo) then
            SetMethodProp(ObjTo, PropInfo, MethodVal);
        end
      end
    end
    finally
      FreeMem(PropInfos, Count * SizeOf(PPropInfo));
    end;
    end;
    

    【讨论】:

      【解决方案3】:

      您可以将源组件写入流并将其读回目标组件。

      MemStream := TMemoryStream.Create;
      try
        MemStream.WriteComponent(Source);
        MemStream.Position := 0;
        MemStream.ReadComponent(Target);
      finally
        MemStream.Free;
      end;
      

      您可能会遇到重复组件名称的问题。

      【讨论】:

      • @Uwe,你说得对,如果 Source 和 Target 共享同一个父级,重复的组件名称将是一个问题。一种解决方案是在将 Source 组件名称写入 Stream 之前将其临时设置为空字符串。阅读完 Target 组件后,如果要持久化目标组件,则必须找到目标组件的专有名称,因为 Delphi 不会流式传输具有空名称属性的组件。
      【解决方案4】:

      在运行时复制现有组件实际上相当容易。困难的部分是将它们所有已发布的属性复制到新的(复制的)对象中。

      对不起,我的代码示例在 C++Builder 中。 VCL 是一样的,只是语言不同。用Delphi翻译应该不会太麻烦:

      for (i = 0; i < ComponentCount; ++i) {
          TControl *Comp = dynamic_cast<TControl *>(Components[i]);
          if (Comp) {
              if (Comp->ClassNameIs("TLabel")) {
                  TLabel *OldLabel = dynamic_cast<TDBEdit *>(Components[i]);
                  TLabel *NewLabel = new TLabel(this);  // new label
                  // copy properties from old to new
                  NewLabel->Top = OldLabel->Top;
                  NewLabel->Left = OldLabel->Left;
                  NewLabel->Caption = Oldlabel->Caption
                  // and so on...
              } else if (Comp->ClassNameIs("TPanel")) {
                  // copy a TPanel object
              }
      

      也许有人有更好的方法将旧控件的所有已发布属性复制到新控件。

      【讨论】:

        猜你喜欢
        • 2015-02-07
        • 1970-01-01
        • 1970-01-01
        • 2014-03-24
        • 1970-01-01
        • 2013-06-02
        • 1970-01-01
        • 2016-10-26
        • 2012-12-19
        相关资源
        最近更新 更多