Problèmes classe dérivée de TPersistent

Résolu
ThWilliam Messages postés 418 Date d'inscription mardi 3 janvier 2006 Statut Membre Dernière intervention 26 novembre 2013 - 19 avril 2006 à 14:18
ThWilliam Messages postés 418 Date d'inscription mardi 3 janvier 2006 Statut Membre Dernière intervention 26 novembre 2013 - 20 avril 2006 à 23:12
Bonjour à tous.

Les variables de type Record ne pouvant pas être publiées, je crée une classe dérivée de TPersistent :
TMyClass = class(TPersistent)
private
FVar1: integer;
FVar2: TColor;
...
published
property Var1: integer read FVar1 write FVar1;
property Var2: TColor read FVar2 write FVar2;
...
end;

TMyCompo = class(TGraphicControl)
private
FMyProp: TMyClass;
published
property MyProp: TMyClass read FMyProp write FMyProp;
end;

Dans le constructeur de TMyCompo: FMyProp:= TMyClass.Create;
Cela marche parfaitement.

Mais si je modifie la propriété MyProp :
property MyProp: TMyClass read FMyProp write SetMyProp;

procedure TMyCompo.SetMyProp (AValue: TMyClass);
begin
FMyProp.Assign(AValue);
Invalidate;
end;

...alors l'assignation des valeurs se fait toujours bien, mais la procedure SetMyProp n'est pas appelée --> pas d'invalidate.
Je n'ai pas trouvé de solution. Comment faire ?
Merci d'avance.

11 réponses

florenth Messages postés 1023 Date d'inscription dimanche 1 août 2004 Statut Membre Dernière intervention 17 août 2008 3
19 avril 2006 à 14:52
C'est normal, car ce n'est pas ton objet TmyClass que tu changes mais ses propriétés. tu dois sonc définir des Setter pour TMyClass.
Bref, pas simple à expliquer, je reprend ton exemple, tu vas mieux comprendre :
<hr size= "2" width="100%">TMyCompo = class ;

TMyClass = class(TPersistent)
private
FVar1: integer;
FVar2: TColor;
FOwner: TMyCompo;
procedure Changed;
{ Setters. }
procedure SetVar1(Value: Integer);
procedure SetVar2(Value: TColor);
published
constructor Create(AOwner: TMyCompo); // Je redéfinis le constructeur.
property Var1: Integer read FVar1 write FVar1;
property Var2: TColor read FVar2 write FVar2;
end;

TMyCompo = class (TGraphicControl)
private
FMyProp: TMyClass;
published
property MyProp: TMyClass read FMyProp;
end;

implementation

constructor TMyClass.Create(AOwner: TMyCompo);
begin
inherited;
{ en fait, on garde la référence du composant pour
pouvoir appeler sa méthode Invalidate(). }
FOwner : = AOwner;
end;

procedure
TMyClass.
Changed;
begin
{ hop, on demande au compo de se redessiner. }
FOwner.Invalidate;
end;

procedure
TMyClass.
SetVar1(Value: Integer);
begin
FVar1 := Value;
Changed; // signalement de changement.
end ;

procedure
TMyClass.
SetVar2(Value: TColor);

begin

FVar2 : = Value;

Changed; // signalement de changement.

end;

<hr size="2" width="100%">
Et voila !
Bon là, j'ai enlevé la possibilité de changer TMyCompo.MyProp mais tu peux le remettre si tu veux.

Gestion de mémoire:
- Le TMyClass ne doit PAS libérer le TMyCompo dans le destructeur.
- Le TMyCompo DOIT libérer le TMyclass dans son destructeur.

/!\: Attention aux fuites de mémoire sinon ! ;-)
Il faut faire attention à ne pas transmetre 'nil' au constructeur de l'objet sinon plantage assuré.

La toute première ligne de code est une déclaration avancée. C'est nécessaire car TMyclass utilise TMyCompo et inversement.
Si tu ne comprends pas cette ligne, c'est pas bien grave, fais juste un petit tour dans l'aide de delphi

J'espère que ça va t'aider.
N'hésite pas si tu as des difficultés à reposer une quesiton

@ ++
Florent

Si tu ne te plantes pas ......
tu ne pousseras jamais
3
Cirec Messages postés 3833 Date d'inscription vendredi 23 juillet 2004 Statut Modérateur Dernière intervention 18 septembre 2022 50
19 avril 2006 à 15:11
Salut,
procedure TMyClass.
Changed;
begin
{ hop, on demande au compo de se redessiner. }
If Assigned(FOwner) Then // re-hop, pas de problème si 'Nil' est transmis au constructeur ;-)

FOwner.Invalidate;
end;


@+
Cirec
0
florenth Messages postés 1023 Date d'inscription dimanche 1 août 2004 Statut Membre Dernière intervention 17 août 2008 3
19 avril 2006 à 15:22
@ Cirec: c'est pour cela que j'ai précisé "Il faut faire attention à ne pas transmetre 'nil' au constructeur de l'objet sinon plantage assuré"
Mais entre nous, ça n'as pas d'interet.
Je préfererais même l'utilisation de la procedure Assert() qui elle, génère une reerur facilement repèrable.
Parce qu'avec ton test Cirec, si on transmet nil su constructeur, on verra rien et on peut passer des heures à vérifier ou ça me***.
Là, pour cette taille de projet, j'imagine que c'estpas trop dur mais j'ai des unités qui dépassent les 2500 lignes et là, va trouver ton problème si ça marche pas comme il faut mais que ça génère aucune exception !!

++

Si tu ne te plantes pas ......
tu ne pousseras jamais
0
ThWilliam Messages postés 418 Date d'inscription mardi 3 janvier 2006 Statut Membre Dernière intervention 26 novembre 2013 4
19 avril 2006 à 21:10
Salut Florent,
Salut Cirec,

Que c'est génial d'apprendre !
Je n'avais pas pensé à la solution de redéfinir le constructeur avec paramètre AOwner.

Pour que TMyClass puisse être employé avec différents composants, j'ai pensé aux modifs suivantes :
FOwner: TComponent;
constructor TMyClass.Create(AOwner: TComponent);
et dans procedure TMyClass.Changed :
with FOwner as TControl do Invalidate;
Cela marche, mais y-at-il une objection ?

Question sur utilisation des resources : est-ce que le fait de créer une classe pour regouper des propriétés en mode conception bouffe beaucoup plus de ressources que le fait d'écrire ces propriétés dans le composant lui-même ?

Grand merci à vous 2.
A +
Thierry
0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
florenth Messages postés 1023 Date d'inscription dimanche 1 août 2004 Statut Membre Dernière intervention 17 août 2008 3
19 avril 2006 à 21:25
Pour ta première suggestion: c'est même une très bonne idée ! Par contre, déclare FOwner comme Tcontrol puisque Invalidate() n'est disponible qu'a partir de ce niveau là.

Si tu ne te plantes pas ......
tu ne pousseras jamais
0
florenth Messages postés 1023 Date d'inscription dimanche 1 août 2004 Statut Membre Dernière intervention 17 août 2008 3
19 avril 2006 à 22:13
"Question sur utilisation des resources : est-ce que le fait de créer
une classe pour regouper des propriétés en mode conception bouffe
beaucoup plus de ressources que le fait d'écrire ces propriétés dans le
composant lui-même ? "

=> J'en ai strictement aucune idée. Ce que je peux te dire par contre, c'est que, comme tout, il ne faut pas en abuser.
De toutes façons, la référence d'une classe est un pointeur donc 4 octets. Comme tu en a un pour le conteneur et un pour le contenu, on peux dire qu'il y aurait environ 8 octets de plus.
Ce qui n'est rien quand on sait que 8 octets correspondent à 3 pixels d'un bitmap (en 24bit/pixel) et que les mémoires des ordi dépassent les 128 Mo soit 128000000 Octets. Alors un de plus, un de moins ...

PS: un Ko (kilo-Octet) est égal à 1000 Octets et non 1024 (2^10). C'est une nouvelle norme qui date d'a peu près un an et du coup, tout le monde confond

Si tu ne te plantes pas ......
tu ne pousseras jamais
0
f0xi Messages postés 4205 Date d'inscription samedi 16 octobre 2004 Statut Modérateur Dernière intervention 12 mars 2022 35
20 avril 2006 à 16:04
voila comment doivent etre tes objets :

(au passage, ça tiens seulement plus de place dans le code ... au final l'utilisation est hyper simplifiée et surtout plus souple et puissante qu'une simple structure record)

<hr size="2" width="100%">

TMyClass = class(TPersistent)
private
fControl : TControl;
fVar1 : integer;
fVar2 : integer; {Tcolor = Integer}
fOnChange : TNotifyEvent;
procedure fSetInt(index : integer; val : integer);
protected
procedure Change; virtual;
procedure AssignTo(Dest : TPersistent); override;
property Control : TControl read FControl;
property OnChange : TNotifyEvent read fOnChange write fOnChange;
published
property Var1: integer index 0 read fVar1 write fSetInt;
property Var2: integer index 1 read fVar2 write fSetInt;
public
constructor Create(Control: TControl); virtual;
destructor Destroy; override;
end;

implementation

constructor TMyClass.Create(Control : TControl);
begin
inherited create;
{code --vvvv-- down}


fControl := Control;
fVar1 := 0;
fVar2 := 0;
end;

destructor TMyClass.Destroy;
begin

{code --^^^^-- up}

inherited destroy;
end;

procedure TMyClass.fSetInt(Index : integer; Val : integer);
begin
case Index of
0: if Val <> fVar1 then begin
fVar1 := Val;
Change;

end;

1: if Val <> fVar2 then begin
fVar2 := Val;
Change;

end;

end;
end;

procedure TMyClasse.Change;
begin
if Assigned(fOnChange) then fOnChange(Self);
end;

procedure TColorBytes.AssignTo(Dest: TPersistent);
begin
if Dest is TMyClass then
with TMyClass(Dest) do begin
fVar1 := Self.fVar1;
fVar2 := Self.fVar2;
Change;
end
else
inherited AssignTo(Dest);
end;

<hr size="2" width="100%">
TMyCompo = class(Tcomponent)
private
fMyProp : TMyClass;
fOnChange : TNotifyEvent;
procedure fSetMyProp(Val : TMyClass);

procedure DoMyPropChange(Sender : TObject);
procedure Change; virtual;
protected
property OnChange : TNotifyEvent read fOnChange write fOnChange;
published
property MyProp : TMyClass read fMyProp write fSetMyProp;
public
constructor Create(AOwner : TComponent); override;
destructor Destroy; override;
end;

constructor TMyCompo.Create(AOwner : TComponent);
begin
inherited Create(AOwner);
{code --vvvv-- down}

fMyProp := TMyClass.Create(nil);
fMyProp.OnChange := DoMyPropChange;

end;


destructor TMyCompo.Destroy;
begin
fMyProp.Free;

{code --^^^^-- up}

inherited Destroy;

end;


procedure TMyCompo.fSetMyProp(Val : TMyClass);

begin
Val.AssignTo(fMyProp);
fMyProp.OnChange(Self);

end;


procedure TMyCompo.DoMyPropChange(Sender : TObject);

begin

{code --^^^^-- up}

Change;

end;


procedure TMyCompo.Change;

begin
if Assigned(fOnChange) then fOnChange(Self);

end;
0
f0xi Messages postés 4205 Date d'inscription samedi 16 octobre 2004 Statut Modérateur Dernière intervention 12 mars 2022 35
20 avril 2006 à 16:10
ps : invalidate ne sert qu'a redessiner le control ... donc totalement inutile dans un composant non visuel.



si il s'agit d'un composant visuel et qu'il est necessaire de
rafraichir l'affichage quand la propriété MyProp est modifiée, il faut
placer le invalidate ici :



procedure TMyCompo.DoMyPropChange(Sender : TObject);
begin
invalidate;

{code --^^^^-- up}
Change;
end;
0
f0xi Messages postés 4205 Date d'inscription samedi 16 octobre 2004 Statut Modérateur Dernière intervention 12 mars 2022 35
20 avril 2006 à 16:12
d'ailleur petit rappel de l'aide delphi a propos de invalidate :

Utilisez Invalidate quand la totalité du contrôle doit être dessinée.Si plusieurs zones d'un contrôle doivent être redessinées,Invalidate provoque le réaffichage de toute la fenêtre en une seule passe,pour éviter les instabilités provoquées par les redessins redondants.Les performances ne sont pas dégradées par plusieurs appels d'Invalidate avant que le contrôle ne soit effectivement redessiné.
0
florenth Messages postés 1023 Date d'inscription dimanche 1 août 2004 Statut Membre Dernière intervention 17 août 2008 3
20 avril 2006 à 18:58
Voila un exemple de la puissance des classes.
Les evenements, c'est mieux dans certains cas. Pour celui-ci, il est vrai que j'ai hésité. Mais ça dépend de l'avenir de la classe: a voir.

Mais par contre, f0xi: si tu crée un evenement, cela ne sert plus a rien d'avoir une reférence à un TControl dans TMyClass.
Serais-ce un oubli de ta part ?

Et puis c'est quoi ce TColorBytes ici : TColorBytes.AssignTo(Dest: TPersistent);
lol
Encore un lapsus j'imagine.
Par contre, dna scette même procédure AssignTo(), tu ne réassigne pas toutes les propriétés: l'évenement par exemple. Et dans ce las là, c'est quand même bien dommage !

Allez, a +
Florent

Si tu ne te plantes pas ......
tu ne pousseras jamais
0
ThWilliam Messages postés 418 Date d'inscription mardi 3 janvier 2006 Statut Membre Dernière intervention 26 novembre 2013 4
20 avril 2006 à 23:12
Salut f0xi,

J'ai un peu de mal avec :
property MyProp : TMyClass read FmyProp write FSetMyProp;
Avec déclaration : property MyProp : TMyClass read FmyProp write FMyProp;
alors je comprends :
en modifiant la valeur de Var1 (p.ex)
--> appel de TMyClass.FSetInt
qui appelle TMyClass.Change
qui appelle l'événement OnChange,
c.à.d. l'exécution de TMyCompo.DoMyPropChange.
Mais avec la procédure FSetMyProp , je ne comprends pas comment se fait l'assignation de valeur (je ne vois pas l'appel à TMyClass.FSetInt).
Bref, je suis dans le brouillard...
Quoiqu'il en soit, plein de choses intéressantes pour moi dans ton code.

A +
Thierry
0
Rejoignez-nous