是否有一种简单的方法可以复制父组件下的所有子组件,包括它们的已发布属性?
例如:
TPanel
的TLabel
TEDIT
TListView的
TSpecialClassX
当然这是最重要的因素,它应该复制我在TPanel上放弃的任何新组件,而不是在正常情况下修改代码.
我听说过RTTI,但实际上从未使用过它.有任何想法吗?
在通过父控件创建循环中的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;
阅读本页面
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;