2008-10-27 4 views
12

Есть ли простой способ дублировать все дочерние компоненты под родительским компонентом, включая их опубликованные свойства?Дублирование компонентов во время выполнения

Например:

  • TPanel
    • TLabel
    • TEdit
    • TListView
    • TSpecialClassX

Конечно, самый важный фактор, он должен дублировать любой новый компонент, который я бросаю на TPanel, не изменяя код при нормальных обстоятельствах.

Я слышал о RTTI, но никогда не использовал его на самом деле. Есть идеи?

ответ

6

есть чтение этой страницы

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; 
0

На самом деле довольно просто дублировать существующие компоненты во время выполнения. Трудная часть - скопировать все опубликованные свойства в новые (дублированные) объекты.

Извините, но мой пример кода находится в 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 
     } 

Может быть, кто-то имеет лучший метод копирования все опубликованные свойств старого управления к новому.

9

Вы можете импользовать CLoneProperties routine from the answer к «Replace visual component at runtime», после того, как вы создали DUP компонентов в петле через управление родителем.

Update: некоторые рабочий код ....

. Я полагаю из вашего вопроса, что вы хотите дублировать элементы управления, которые содержатся в 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; 
+0

Очень хорошее решение! Спасибо за вашу помощь! – 2014-12-16 17:22:24

3

Вы можете написать исходный компонент в поток и прочитать его обратно в целевой компонент.

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

У вас могут возникнуть проблемы с повторяющимися именами компонентов.

+1

@ Uwe, вы правы, что дублирующиеся имена компонентов будут проблемой, если оба источника и Target имеют один и тот же родитель. Одним из решений является временное указание имени исходного компонента на пустую строку перед тем, как записать его в поток. После прочтения целевого компонента вам нужно найти собственное имя для целевого компонента, если вы хотите сохранить целевой компонент, поскольку Delphi не передает компоненты с пустым именем. – iamjoosy 2011-11-29 08:41:47

Смежные вопросы