2010-04-19 5 views
1

J'implémente une fonctionnalité Boilerplate - permet aux utilisateurs de modifier les descriptions de certains composants - comme TLabel s - lors de l'exécution. par exemple.Delphi obtient la valeur des propriétés des composants de formulaire

TFooClass = Class (TBaseClass) 
Label : Tlabel; 
... 
End; 

Var FooClass : TFooClass; 

...

Au moment de la conception, la valeur de la propriété de la légende de l'étiquette est de dire - « Prénom », lorsque l'application est exécutée, il existe une fonctionnalité qui permet à l'utilisateur de changer la légende valeur à dire 'autre nom'. Une fois que cela est modifié, la légende de l'étiquette pour l'instance de classe de FooClass est mise à jour immédiatement. Le problème est maintenant si l'utilisateur pour une raison quelconque veut revenir à la valeur de conception de dire «Prénom», il semble impossible.

Je peux utiliser les méthodes RTTIContext et tout cela, mais je à la fin de la journée, il semble d'exiger que l'instance de la classe pour moi de changer la valeur et puisque ce a déjà changé - il me semble avoir heurté un mur de briques.

Ma question est - est-il un moyen en utilisant les anciennes méthodes RTTI ou les nouveautés RTTIContext à la propriété d'un membre de la classe sans instancier la classe - à savoir obtenir la propriété de la définition ClassType.

Ceci est extrait de code de ma tentative de le faire:

c : TRttiContext; 
    z : TRttiInstanceType; 
    w : TRttiProperty; 
Aform : Tform; 
    .... 
Begin 
..... 

    Aform := Tform(FooClass); 

    for vCount := 0 to AForm.ComponentCount-1 do begin 
    vDummyComponent := AForm.Components[vCount]; 
    if IsPublishedProp(vDummyComponent,'Caption') then begin 
     c := TRttiContext.Create; 
     try 
     z := (c.GetType(vDummyComponent.ClassInfo) as TRttiInstanceType); 
     w := z.GetProperty('Caption'); 
      if w <> nil then 
      Values[vOffset, 1] := w.GetValue(vDummyComponent.ClassType).AsString 
     ..... 
     ..... 

.... 
.... 

Je me fais toutes sortes d'erreurs et toute aide sera grandement appréciée.

Répondre

1

On dirait que ce que vous essayez de faire est d'obtenir la valeur d'une certaine propriété telle que définie dans le DFM. Cela ne peut pas être fait en utilisant RTTI, puisque RTTI est basé sur l'inspection de la structure d'un objet comme spécifié par sa définition de classe. Le DFM ne fait pas partie de la définition de classe; C'est une liste de propriétés qui est appliquée aux objets après leur création à partir des définitions de classe.

Si vous souhaitez obtenir les valeurs des propriétés des contrôles d'un formulaire, vous devrez probablement les mettre en cache quelque part. Essayez de mettre quelque chose dans le formulaire OnCreate qui traverse tous les contrôles et utilise RTTI pour remplir un TDictionary<string, TValue> avec les valeurs de toutes les propriétés. Ensuite, vous pouvez les rechercher plus tard lorsque vous en avez besoin.

+0

Vous avez parfaitement raison de ce que j'essaie de faire. Mon problème est -J'ai beaucoup de formes et je suis sûr que l'implémentation de TDictionary va gonfler la taille de l'application. Afin de le rendre persistant, je suis en train de stocker la valeur modifiée dans une base de données - il se peut que je doive créer une colonne pour stocker la valeur d'origine avant toute modification de la valeur. Je pense toujours que le pointeur ClassInfo aurait les valeurs des valeurs de temps de conception pour les contrôles bien. –

+0

@Kayode: Le pointeur ClassInfo ne peut pas avoir des valeurs de conception pour les objets, car ils n'appartiennent pas aux classes, ils appartiennent à des instances spécifiques. Par exemple, que se passe-t-il si vous avez deux TLabels différents sur un formulaire, avec deux légendes différentes? Lequel voulez-vous qu'il stocke? –

+0

Je pensais en fait à obtenir le classinfo du propriétaire qui encapsule ces contrôles, donc j'ai deux contrôles TLabel au moment du design, leur état de conception serait encapsulé dans ClassInfo pour la classe Owner. –

0

Si vous tentez de l'atteindre pour restaurer la valeur définie au moment de la conception (c'est-à-dire celle qui est enregistrée dans le DFM), j'utiliserais InitInheritedComponent comme point de départ.

Il est possible d'obtenir le contenu du DFM lors de l'exécution. Cela pourrait être une douleur à analyser.

Cochez également InternalReadComponentRes.

Les deux routines peuvent être trouvées dans l'unité de classes.

+0

J'ai envisagé de mettre cela dans ma réponse, mais ces routines sont pour lire une ressource DFM entière à la fois. Et pour économiser de l'espace, le compilateur compresse les DFM au format binaire. Difficile d'analyser en effet! –

+0

C'est pourquoi quelqu'un qui doit "analyser" le DFM binaire aura probablement besoin d'utiliser une des fonctions de niveau inférieur que Delphi utilise pour initialiser les formulaires, ou peut-être qu'un simple TReader fera le travail. Je pense toujours que c'est le meilleur chemin à suivre si ça fonctionne. –

1

Le système RTTI ne fournit pas ce que vous recherchez. Les informations de type ne sont actuellement déterminées qu'au moment de la compilation. Les valeurs de formulaire initiales sont définies lors de l'exécution à l'aide de la ressource DFM. Vous pouvez modifier les valeurs dans une ressource DFM dans une application compilée car elle est évaluée au moment de l'exécution.

Analyser et utiliser la ressource DFM dans laquelle elle est stockée ou effectuer une copie de la valeur d'origine lors de l'exécution. Peut-être au moment du changement initial pour réduire votre empreinte mémoire.

Maçons La suggestion d'utiliser TDictionary<string, TValue> est ce que j'utiliserais. Je ferais attention de stocker cette information dans une base de données car la garder synchronisée pourrait devenir un vrai cauchemar de maintenance.

0

Bien - J'ai résolu le problème. L'astuce est instancié essentiellement une autre instance de la forme comme ceci:

procedure ShowBoilerPlate(AForm : TForm; ASaveAllowed : Boolean); 
var 
    vCount   : Integer; 
    vDesignTimeForm : TForm; 
    vDesignTimeComp : TComponent; 
    vDesignTimeValue : String; 
    vCurrentValue : String; 
begin 
    .... 
    .... 
    vDesignTimeForm := TFormClass(FindClass(AForm.ClassName)).Create(AForm.Owner); 

    try 
    // Now I have two instances of the form - I also need to have at least one 
    // overloaded constructor defined for the base class of the forms that will allow for 
    // boilerplating. If you call the default Constructor - no boilerplating 
    // is done. If you call the overloaded constructor, then, boilerplating is done. 
    // Bottom line, I can have two instances AForm - with boilerplated values and 
    // vDesignForm without boilerplated values. 
    for vCount := 0 to AForm.ComponentCount-1 do begin 
     vDummyComponent := AForm.Components[vCount]; 
     if Supports (vDummyComponent,IdoGUIMetaData,iGetGUICaption) then begin 
      RecordCount := RecordCount + 1; 
      Values[vOffset, 0] := vDummyComponent.Name; 
      if IsPublishedProp(vDummyComponent,'Caption') then begin 
      vDesignTimeComp := vDesignTimeForm.FindComponent(vDummyComponent.Name); 
      if vDesignTimeComp <> nil then begin 
      // get Design time values here 
       vDesignTimeValue := GetPropValue(vDesignTimeComp,'Caption'); 
      end; 
      // get current boilerplated value here 
       vCurrentValue := GetPropValue(vDummyComponent,'Caption'); 
     end; 
     vOffset := RecordCount;; 
     end; 
    end; 

    finally 
    FreeAndNil(vDesignTimeForm); 
    end; 
end; 

Quoi qu'il en soit - Merci à tous pour tous vos conseils.

+0

Veuillez donc accepter votre réponse comme la bonne, afin que nous puissions facilement voir le problème a été résolu. Voir [acceptation des réponses] (http://meta.stackoverflow.com/questions/5234/how-does-accepting-an-answer-work). ;) – bluish

Questions connexes