简体   繁体   English

在运行时复制组件

[英]Duplicating components at Run-Time

Is there a simple way to duplicate all child components under parent component, including their published properties? 是否有一种简单的方法可以复制父组件下的所有子组件,包括它们的已发布属性?

For example: 例如:

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

Of course the most important factor, it should duplicate any new component which I drop on the TPanel without modifying the code under normal circumstances. 当然这是最重要的因素,它应该复制我在TPanel上放弃的任何新组件,而不是在正常情况下修改代码。

I've heard of the RTTI, but never used it actually. 我听说过RTTI,但实际上从未使用过它。 Any ideas? 有任何想法吗?

You can propably use the CLoneProperties routine from the answer to " Replace visual component at runtime ", after you have created the dup components in a loop thru the parent's controls. 在通过父控件创建循环中的dup组件之后,您可以在运行时替换可视组件的答案中使用CLoneProperties例程

Update : some working code.... 更新 :一些工作代码....

. I assume from your question that you want to duplicate the Controls that are contained in a WinControl (as a Parent is a TWinControl). 我假设您要复制WinControl中包含的控件(作为父控件是TWinControl)。
. As I did not know if you also wanted to hook the duplicated controls with the same Event Handlers as the originals, I made an option for that. 因为我不知道你是否也希望用与原件相同的事件处理程序来挂钩重复的控件,所以我为此做了一个选项。
. And you may want to give a proper meaningful Name to the duplicated controls. 并且您可能希望为重复的控件提供适当的有意义的名称。

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;

have a read of this page 阅读本页面

Run-Time Type Information In Delphi - Can It Do Anything For You? Delphi中的运行时类型信息 - 它可以为您做任何事情吗?

Noting the section Copying Properties From A Component To Another 注意“ 将组件复制到另一个组件 ”一节

which has a unit, RTTIUnit with a Procedure, which seems to do part of what you want but i don't think it will copy any child components with out extra code. 它有一个单元,带有一个过程的RTTIUnit,它似乎可以做你想要的一部分,但我认为它不会复制任何子组件而没有额外的代码。 (i think its ok to paste it here...) (我认为可以将它贴在这里......)

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;

You can write the source component into a stream and read it back into the target component. 您可以将源组件写入流并将其读回目标组件。

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

You may get problems with duplicate component names though. 但是,您可能会遇到重复组件名称的问题。

It's actually fairly easy to duplicate existing components at runtime. 实际上,在运行时复制现有组件相当容易。 The difficult part is to copy all of their published properties to the new (duplicated) objects. 困难的部分是将所有已发布的属性复制到新的(重复的)对象。

I'm sorry, but my code example is in C++Builder. 对不起,我的代码示例是在C ++ Builder中。 The VCL is the same, just a different language. VCL是一样的,只是一种不同的语言。 It shouldn't be too much trouble to translate it Delphi: 翻译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
        }

Maybe somebody has a better method of copying all of the published properties of the old control to the new one. 也许有人有更好的方法将旧控件的所有已发布属性复制到新控件。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM