2015-12-11 1 views
2

Comment définir la valeur par défaut d'une propriété d'objet de composant?Delphi 2009: Valeur par défaut de la propriété Component Object

Code de classe Composant:

unit CustomClass; 

interface 

uses 
    Classes; 

type 
    TCustomClass = class; 

    TConfiguration = class; 

    TCustomClass = class (TComponent) 
    private 
    FConfiguration: TConfiguration; 
    procedure SetConfiguration(const Value: TConfiguration); 
    published 
    property Configuration: TConfiguration read FConfiguration write SetConfiguration; 
    public 
    constructor Create(AOwner: TComponent); override; 
    destructor Destroy; override; 
    end; 

    TConfiguration = class (TPersistent) 
    private 
    FDelay: Integer; 
    FSize: Integer; 
    procedure SetDelay(const Value: Integer); 
    procedure SetSize(const Value: Integer); 
    published 
    property Delay: Integer read FDelay write SetDelay; 
    property Size: Integer read FSize write SetSize; 
    public 
    procedure Assign(Source: TPersistent); override; 
    end; 

implementation 

{ TCustomClass } 

constructor TCustomClass.Create(AOwner: TComponent); 
begin 
    inherited; 
    Configuration.FileName:= 'FileName'; 
    Configuration.Size := 10; 
end; 

destructor TCustomClass.Destroy; 
begin 
    Configuration.Free; 
    inherited; 
end; 

procedure TCustomClass.SetConfiguration(const Value: TConfiguration); 
begin 
    FConfiguration.Assign(Value); 
end; 

{ TConfiguration } 

procedure TConfiguration.Assign(Source: TPersistent); 
begin 
    inherited; 
    Delay := (Source as TConfiguration).Delay; 
    Size := (Source as TConfiguration).Size; 
end; 

procedure TConfiguration.SetDelay(const Value: Integer); 
begin 
    FDelay := Value; 
end; 

procedure TConfiguration.SetSize(const Value: Integer); 
begin 
    FSize := Value; 
end; 

end. 

Dans mon IDE, il apparaîtra comme a été modifié la propriété d'objet (gras bleu).

je pensais à faire défaut et fonctions stockées sur les propriétés de la classe TConfiguration, comme ceci:

TConfiguration

interface 

private 
    function FileNameStored: Boolean; 
published 
    property FileName: string read FFileName write SetFileName stored FileNameStored; 
    property Size: Integer read FSize write SetSize default 10; 

implementation 

function TConfiguration.FileNameStored: Boolean; 
begin 
    Result := FileName <> 'FileName'; 
end; 

Il colore les propriétés de TConfiguration en bleu normal, mais pas la propriété de configuration de TCustomClass et ce n'est pas là que je veux définir ses valeurs par défaut, est sur TCustomClass, puisque TConfiguration peut être utilisé dans d'autres composants.

Puis, je pensais aussi:

TCustomClass

interface 

private 
    function ConfigurationStored: Boolean; 
published 
    property Configuration: TConfiguration read FConfiguration write SetConfiguration stored ConfigurationStored; 

implementation 

function TCustomClass.ConfigurationStored: Boolean; 
begin 
    Result := Configuration.FileName <> 'FileName' and 
    Configuration.Size <> 10; 
end; 

Mais, cela ne définit que la couleur de la propriété Configuration TCustomClass à bleu normal, pas ses propriétés.

RÉPONSE

Comme @RemyLebeau a fait remarquer (dans le premier et le haut réponse), il y avait des erreurs dans le code. Dans ce composant et dans ce cas, j'ai décidé de ne pas définir de valeur par défaut pour les propriétés.

+0

Aviez-vous l'intention de déposer un objet de configuration sur un formulaire et de déposer la classe personnalisée sur un formulaire, puis de les connecter? Ou vouliez-vous que l'objet de configuration soit une propriété appartenant à la classe personnalisée et qu'il ne soit pas nécessaire de le créer et de l'assigner manuellement? –

+1

Ensuite, vous avez vraiment oublié certaines choses évidentes, comme le souligne Remy. Il montre que vous devez créer explicitement (dans votre constructeur de classe) et libérer (dans votre destructeur de classe) TObject-types qui appartiennent à (sont une partie indivisible de) votre objet. Vous avez eu la classe de base correcte, donc c'est bien. Vous pouvez ajouter un OnChange: TNotificationEvent à votre objet de configuration. Il peut donc appeler la classe principale et faire quelque chose quand quelqu'un écrit dans une propriété de configuration. –

+0

@WarrenP Oui. Je l'ai corrigé, mais comme @NGLN l'a souligné, cela n'a pas de sens de le corriger sur la question, car cela rend la réponse inutile.Le 'OnChange: TNotificationEvent' est vraiment un bon conseil, je l'ai aimé, car je peux changer la valeur' stored' des propriétés si elles sont une variable privée de classe. Merci. –

Répondre

5

Il y a plusieurs bugs dans votre code.

  1. votre constructeur TCustomClass ne crée pas l'objet TConfiguration. Vous avez besoin d'ajouter que:

    constructor TCustomClass.Create(AOwner: TComponent); 
    begin 
        inherited; 
        FConfiguration := TConfiguration.Create; // <-- add this 
        FConfiguration.FileName := 'FileName'; 
        FConfiguration.Size := 10; 
    end; 
    

    Cela étant dit, l'affectation des propriétés FileName et Size devrait probablement être déplacé au constructeur TConfiguration plutôt que TCustomClass constructeur.

  2. Une propriété String ne peut pas être définie avec une valeur par défaut définie par l'utilisateur, la valeur par défaut est toujours une chaîne vide. Ainsi, votre propriété FileName apparaîtra toujours comme modifiée lors de la création de votre composant, car votre constructeur lui attribue une valeur autre que celle par défaut. Votre approche stored est la bonne solution pour gérer cela. Ou, n'attribuez simplement pas un FileName at all par défaut, laissez-le vide. Si l'utilisateur n'attribue pas explicitement FileName, votre code peut prendre une valeur par défaut si nécessaire.

    Une propriété Integer, d'autre part, peut être définie avec une valeur par défaut définie par l'utilisateur. Votre propriété Size ne fait pas cela, cependant.Il devrait être (surtout si vous déplacez l'affectation de la valeur par défaut dans le constructeur TConfiguration):

    property Size: Integer read FSize write SetSize default 10; 
    
  3. votre méthode TConfiguration.Assign() est mis en œuvre de manière incorrecte. En appelant la méthode Assign() héritée avant de copier les valeurs, votre code lèvera toujours une exception EConvertError lors de l'exécution si l'appelant affecte un objet TConfiguration à un autre. La raison en est que l'implémentation TPersistent.Assign() de la classe de base appelle simplement Source.AssignTo(Self), et TConfiguration ne remplace pas la méthode AssignTo(), donc TPersistent.AssignTo() est appelée, ce qui appelle simplement Dest.AssignError(Self), ce qui déclenche l'exception. Alors, ne pas appeler la méthode héritée Assign() si le Source est en fait un objet TConfiguration:

    procedure TConfiguration.Assign(Source: TPersistent); 
    begin 
        if Source is TConfiguration then 
        begin 
        FDelay := TConfiguration(Source).Delay; 
        FSize := TConfiguration(Source).Size; 
        end else 
        inherited; 
    end; 
    
+0

Pourquoi devrais-je déplacer l'affectation TConfiguration du constructeur TCustomClass vers le constructeur TConfiguration? Est-ce un moyen conventionnel lors de l'écriture des classes de composants? Car qu'est-ce que je fais si à l'avenir je crée un autre composant et utilise cette même classe TConfiguration? –

+2

Une propriété de classe est une caractéristique de cette classe. Une valeur par défaut de cette propriété aussi. C'est déjà assez de raison. Mais surtout lorsque vous avez besoin d'utiliser cette classe dans plusieurs scénarios, vous devez la concevoir pour être autonome. Ainsi vous créez un 'TConfiguration' dans la classe où vous en avez besoin, et là vous pouvez assigner des paramètres personnalisés, mais les paramètres par défaut sont faits dans' TConfiguration' lui-même. – NGLN

+1

C'est entièrement à vous. Si une pomme verte doit être rouge dans un panier de fruits, alors faites-la rouge tout en remplissant le panier. Mais si une pomme verte devrait avoir des taches rouges, alors vous devriez concevoir une pomme tachetée capable d'avoir des taches avant de faire le panier. Ou: si peu importe le type de banane que contient le panier, enregistrez le type de banane après avoir rempli le panier. Mais quand il n'y a que des bananes Chiquita dans le panier, alors appliquer cette restriction en concevant un TChiquitaBanana de TBanana spécifiquement pour ce type de panier. – NGLN

0

Mais, cela ne définit que la TCustomClassConfiguration propriété couleur bleu à la normale, pas ses propriétés.

C'est intentionnellement. Un storage specifier ne fonctionne que pour la propriété pour laquelle il est spécifié.

Comparez votre classe avec une propriété TFont sur un formulaire. Lorsque ParentFont est True, la propriété Font ne sera pas stockée. Mais ses membres Color et Name ne sont toujours pas par défaut, ils apparaissent donc comme s'ils seraient stockés. C'est parce qu'un objet TFont ne connaît pas son propriétaire, comment le pourrait-il? Une fois, il fait partie d'une toile, l'autre fois il fait partie d'un contrôle ou n'a aucun propriétaire. Dans toutes ces conditions, l'objet TFont doit être entièrement fonctionnel, il ne demande donc pas à ses parents comment il doit se comporter. De l'autre côté: le parent ne devrait pas interroger son enfant sur la façon de se comporter aussi.

Maintenant de retour à votre scénario: si les propriétés FileName et Size doivent être stockées doivent être traitées dans TConfiguration. Si la propriété Configuration de TCustomClass doit être stockée peut pas dépendent de ces propriétés FileName et Size. Si la propriété Configuration est stockée (est en gras), mais que tous ses membres ne sont pas stockés (pas en gras), rien n'est stocké à la fin. Si vous avez une propriété ParentConfiguration, comme ParentFont, vous pouvez décider de ne pas stocker la propriété Configuration. Sinon, laissez-le être et laissez l'audace ne vous distrait plus.

+0

Vos mots sont vrais. Mais dans cette citation, j'ai essayé d'expliquer que l'approche de la propriété 'Configuration' stockée ne suffisait pas à satisfaire mes besoins car elle ne rendait que la 'Configuration' bleue normale et non ses propriétés - qui resteraient bleues. –