2008-10-27 7 views
12

Existe-t-il un moyen simple de dupliquer tous les composants enfants sous le composant parent, y compris leurs propriétés publiées?Duplication de composants au moment de l'exécution

Par exemple:

  • TPanel
    • TLabel
    • TEdit
    • TListView
    • TSpecialClassX

Bien sûr, le facteur le plus important, il devrait reproduire tout nouveau composant que je laisse tomber sur le TPanel sans modifier le code dans des circonstances normales.

J'ai entendu parler du RTTI, mais je ne l'ai jamais utilisé. Des idées?

Répondre

6

ont une lecture de cette page

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

Notant la section Copying Properties From A Component To Another

qui a une unité, RTTIUnit une procédure qui semble faire partie de ce que vous voulez, mais je n » Je pense qu'il va copier tous les composants enfants sans code supplémentaire. (je pense que son ok pour le coller ici ...)

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

En fait, il est assez facile de dupliquer des composants existants lors de l'exécution. La partie difficile consiste à copier toutes leurs propriétés publiées dans les nouveaux objets (dupliqués).

Je suis désolé, mais mon exemple de code est dans C++ Builder. La VCL est la même, juste une langue différente. Il ne devrait pas être trop compliqué de traduire 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 
     } 

Peut-être que quelqu'un a une meilleure méthode de copie toutes les propriétés publiées de l'ancien contrôle à la nouvelle.

9

Vous pouvez utiliser le propably CLoneProperties routine from the answer à « Replace visual component at runtime », après avoir créé les composants dupliqués dans une boucle à travers les commandes du parent.

Mise à jour: un code de travail ....

. Je suppose de votre question que vous voulez dupliquer les contrôles qui sont contenus dans un WinControl (comme un Parent est un TWinControl).
. Comme je ne savais pas si vous vouliez aussi accrocher les contrôles dupliqués avec les mêmes gestionnaires d'événements que les originaux, j'ai fait une option pour cela.
. Et vous voudrez peut-être donner un nom significatif aux contrôles dupliqués.

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

Très bonne solution! Merci de votre aide! –

3

Vous pouvez écrire le composant source dans un flux et le relire dans le composant cible.

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

Cependant, vous pouvez rencontrer des problèmes avec les noms de composants en double.

+1

@ Uwe, vous avez raison de dire que les noms de composants en double seront un problème si Source et Cible partagent le même parent. Une solution consiste à définir temporairement le nom du composant Source sur une chaîne vide avant de l'écrire dans un flux. Après avoir lu le composant cible, vous devez trouver un nom de fichier pour le composant cible si vous souhaitez conserver le composant cible car Delphi ne diffuse pas de composants avec une propriété de nom vide. – iamjoosy

Questions connexes