在运行时复制组件

Atl*_*las 12 delphi rtti

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

例如:

  • TPanel
    • 的TLabel
    • TEDIT
    • TListView的
    • TSpecialClassX

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

我听说过RTTI,但实际上从未使用过它.有任何想法吗?

Fra*_*ois 9

在通过父控件创建循环中的dup组件之后,您可以 " 在运行时替换可视组件 " 的答案中使用CLoneProperties例程.

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

.我假设您要复制WinControl中包含的控件(作为父控件是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;
Run Code Online (Sandbox Code Playgroud)


Chr*_*ase 6

阅读本页面

Delphi中的运行时类型信息 - 它可以为您做任何事情吗?

注意" 将组件复制到另一个组件 "一节

它有一个单元,带有一个过程的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;
Run Code Online (Sandbox Code Playgroud)


Uwe*_*abe 5

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

MemStream := TMemoryStream.Create;
try
  MemStream.WriteComponent(Source);
  MemStream.Position := 0;
  MemStream.ReadComponent(Target);
finally
  MemStream.Free;
end;
Run Code Online (Sandbox Code Playgroud)

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

  • @ Uwe,你是对的,如果源和目标共享相同的父级,重复的组件名称将成为问题。一种解决方案是在将 Source 组件名称写入 Stream 之前暂时将其设置为空字符串。阅读目标组件后,如果您想保留目标组件,则必须找到目标组件的正确名称,因为 Delphi 不会流式传输具有空名称属性的组件。 (2认同)