Tutorial - Développer des composants
Transcription
Tutorial - Développer des composants
[DELPHI] Développer des composants
On va passer à des choses très intéressantes en Delphi. Mais pour cela, il vous faut
IMPERATIVEMENT relire le tutorial 191 sur le développement de classes, car on va réutiliser
les résultats.
Contenu
I) Dérivation des classes
II) Création de propriétés
III) Dessiner le composant
IV) Propriétés héritées
V) Evènements hérités
VI) Créer ses propres évènements
VII) Intercepter les messages
VIII) Logiciel de création de composants
IX) Conclusion
I) Dérivation des classes
Un composant est une ressource qui a pour tâche d'effectuer des traitements et des calculs
précis. C'est donc un encapsulement thématique. Certains sont non visuels (ils sont simples
et indépendant des messages de Windows) et d'autres sont visuels (vous les placez
visuellement comme vous aviez l'habitude de faire).
Or, pour développer un composant, il faut un socle déjà fait. Imaginez qu'on parte de rien
(class...private...public...end) : alors il faudrait regérer la souris, l'affichage, les fonctions de
base... Bref, il y aurait de la redondance absolument chiante, farfelu et superflue.
Merci Delphi de nous proposer 4 classes fondamentales pour entamer le développement de
composants. Il vous faut déjà cibler ce que vous voulez : visuel ou pas visuel ? Pour les
visuels, ils seront référencés dans la palette de composants et vous pourrez voir leur
apparence en fonction des modifications apportées au niveau des propriétés. Les non visuels
auraient pu se cantonner à un simple fichier PAS qu'on aurait réutilisé via la clause USES (cf.
classroom.pas du tuto précédent), mais le fait de le mettre en tant que composant, permet
d'avoir accès facilement à ses propriétés (les plus importantes généralement) dans
l'inspecteur d'objets de Delphi.
Voici les 4 classes :
TComponent : Sert pour les composants non visuels (TOpenDialog, TSaveDialog,
TTimer...). Delphi les détectent automatiquement et les affiche dans un petit carré placé audessus de tous les autres composants.
TGraphicControl : Sert pour les composants visuels en non intéraction avec les messages
de Windows. Ces composants ne sont pas fenêtrés (ils n'ont pas de Handle) et Windows ne
les considère donc pas comme une fenêtre. Ces composants ne peuvent pas recevoir de
focalisation et l'utilisateur n'ayant pas de souris (imaginons !) ne pourra jamais cliquer
dessus. Ils prennent peu de mémoire et sont utiles pour faire des affichages au sein de
composants simples.
TWinControl : Sert à l'interception des messages de Windows. Cette classe est le
composant fenêtré le plus simple qu'il soit, mais ne peut pas servir proprement au
développement de composant, car il ne propose pas de quoi dessiner dedans. Tout
composant fenêtré recoit des messages de la part de Windows. En conséquence, cette
classe sert à les recupérer. Bien que ce composant soit visuel, il est souvent caché.
TCustomControl : C'est le TGraphicControl fenêtré. Il occupe plus de mémoire, peut
recevoir des focalisations... Il dérive de TWinControl et propose une zone pour dessiner.
N'abusons pas de ce type.
Pour choisir entre TGraphicControl et TCustomControl, il faut planifier nos projets. Pour une
barre de progression simple, il existe : TGauge et TProgressBar qui sont respectivement un
TGraphicControl et un TWinControl. En fait, TGauge est optimisée et est une alternative
simple pour sa tâche. TProgressBar est un composant géré par les DLL de Windows.
Windows étant du C++, tous les composants sont nécessairement des fenêtres. Si vous
remarquez bien, les composants de l'onglet Standard sont gérés entièrement par Windows.
C'est justifié : avec XPman vous pouvez changer leur apparence mais vous n'avez pas pour
autant modifié leur code source !!! Dans leur ordre de présentation, vous avez les équivalents
C++ suivant : [TMainMenu], [TPopupMenu], Static, Edit, Memo, Button, Button [TCheckBox],
Button [TRadioButton], ListBox, Combo, ScrollBar, [TGroupBox], [TRadioGroup], [TPanel].
L'utilisation des fenêtres est indispensable pour cette gestion.
On résumera ainsi : par défaut, choississez TGraphicControl. Si vous devez gérer des focus,
alors vous mettez TCustomControl. De toute façon, passer de l'un à l'autre ne crée pas de
problème, car ces classes proposent les mêmes fonctionnalités, au détail près, que
TCustomControl est fenêtré et propose des choses en plus.
Note importante : les composants fenêtrés peuvent être espionés et manipulés par des
logiciels parasiteurs. Ainsi leur comportement peut être modifié. Donnons l'exemple d'un
bouton désactivé (Enabled:=false) sur lequel l'utilisateur a quand même réussi à cliquer
dessus. On verra ça un peu plus tard quand vous aurez bien compris certaines choses. Un
TGrapicControl n'est pas sujet à ces problèmes (non fenêtrés, je le rappelle).
II) Création de propriétés
!!! AVERTISSEMENT !!!
Contrairement au tutorial précédent (N°191), les classes sont ici dérivées, donc ça
va changer...
Créons un composant avec une propriété initialisée à TRUE :
unit MonCompo;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs;
type
TMonCompo = class(TGraphicControl)
private
FProp : boolean;
public
constructor Create(AOwner:TComponent); override;
published
property Propriete:boolean read FProp write FProp;
end;
procedure Register;
implementation
constructor TMonCompo.Create(AOwner:TComponent);
begin
inherited Create(AOwner);
Width:=150;
Height:=150;
FProp:=true;
end;
procedure Register;
begin
RegisterComponents('Exemples', [TMonCompo]);
end;
end.
Il faut toujours initialiser ses variables, sinon ça devient du n'importe quoi.
J'ai mis en couleur les nouveautés :
1) class(TGraphicControl) parce qu'on fait un composant graphique
2) override apparaît seulement ici dans le code. Il indique à Delphi qu'on va remplacer le
CREATE de TGraphicControl tout en gardant ce qu'il y avait originellement. L'appel à ce
passé se fait via INHERITED suivi de CREATE avec tout son paramétrage. Le paramète
AOwner indique la parenté du composant (de quel composant il dépend afin de pouvoir faire
la transmission des messages). Ces liens se schématisent facilement : Form1.Refresh doit
permettre aux sous-composants d'être également redessinés.
3) Register est bien un nom de procédure (confus mais habituel)
4) inherited est déjà expliqué
5) Width et Height (hérités de TGraphicControl) permettent de spécifier une taille minimale
du composant quand on le crée. Ainsi, l'utilisateur qui clique une fois sur la palette puis un
fois sur la fiche fait comme s'il avait tracé un rectangle de 150x150.
Le destructeur demande aussi un override. Il faut toujours garder à l'esprit que la
dérivation nous offre un certain nombre de propriétés qu'il faut préserver avec soins. La
preuve est qu'on utilise WIDTH et HEIGHT alors que ça n'apparaît pas directement dans la
classe.
Note importante : par habitude, toutes les variables de PRIVATE doivent commencer par la
lettre "F".
N'oubliez pas non plus qu'on peut utiliser GetQqch et SetQqch dans les READ et WRITE.
III) Dessiner le composant
Ca ne marche que pour les composants visuels : TGraphicControl et TCustomControl.
Ca se fait via la procédure Paint écrite dans le PROTECTED.
unit MonCompo;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs;
type
TMonCompo = class(TGraphicControl)
private
FProp : boolean;
protected
procedure Paint; override;
public
constructor Create(AOwner:TComponent); override;
published
property Propriete:boolean read FProp write FProp;
end;
procedure Register;
implementation
constructor TMonCompo.Create(AOwner:TComponent);
begin
inherited Create(AOwner);
Width:=150;
Height:=150;
FProp:=true;
end;
procedure TMonCompo.Paint;
begin
inherited Paint;
//on dessine
end;
procedure Register;
begin
RegisterComponents('Exemples', [TMonCompo]);
end;
end.
L'accès à la procédure Paint efface l'arrière-plan automatiquement. Là où est marqué "on
dessine", l'arrière-plan est de la couleur du Brush du canevas du composant. Après c'est du
dessin statégique avec les propriétés de base des canevas et les propriétés du composant
(passer par les variables F* et non par les publications !!!)
with Canvas do
begin
Pen.Width:=1;
Pen.Color:=clBlack;
Pen.Style:=psSolid;
Brush.Color:=clBlack;
Brush.Style:=bsSolid;
end;
PEN détermine le contour des formes géométriques. BRUSH détermine leur contenu.
Pour un exemple de dessin, voir (par exemple) le lien suivant. C'est assez transparent.
http://www.delphifr.com/code.aspx?ID=20705
!!! AVERTISSEMENT !!!
Dans la procédure PAINT, n'utilisez jamais les fonctions suivantes. Vous risqueriez de
planter Delphi et vos applications (boucle sans fin). Une fois sorti du PAINT, le composant est
automatiquement rafraîchi.
Refresh, Invalidate, Update, Repaint
Ailleurs que dans PAINT, l'appel à ces 4 fonctions entraîne l'appel à PAINT.
IV) Propriétés héritées
L'alignement, la visibilité, les PopupMenu... sont des propriétés protégées (PROTECTED)
mais qui ne sont disponibles que si on les publie. C'était la petite remarque bizarre du tutorial
191. Il suffit simplement de noter leur nom pour les activer :
unit MonCompo;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs;
type
TMonCompo = class(TGraphicControl)
private
FProp : boolean;
protected
procedure Paint; override;
public
constructor Create(AOwner:TComponent); override;
published
property Propriete:boolean read FProp write FProp;
property Align;
property Caption;
property Color;
property Cursor;
property DragCursor;
property DragMode;
property Enabled;
property Font;
property Hint;
property ParentFont;
property ParentShowHint;
property PopupMenu;
property Visible;
end;
procedure Register;
implementation
constructor TMonCompo.Create(AOwner:TComponent);
begin
inherited Create(AOwner);
Width:=150;
Height:=150;
FProp:=true;
end;
procedure TMonCompo.Paint;
begin
inherited Paint;
//on dessine
end;
procedure Register;
begin
RegisterComponents('Exemples', [TMonCompo]);
end;
end.
Certaines propriétés héritées de TCustomControl ne sont pas disponibles dans
TGraphicControl : TabIndex, TabStop...
D'autres ne sont pas disponibles dans certaines versions de Delphi. Il faut alors fait de la
compilation conditionnelle via les directives de compilation, qui, comme leur nom l'indique,
orchestre l'interprétation du code.
V) Evènements hérités
On parle de propriétés héritées, mais les évènements s'héritent également. Voici une liste
non exhaustive :
property
property
property
property
property
property
property
property
property
property
property
property
property
property
property
property
property
property
property
property
property
OnClick;
OnContextPopup;
OnCreate;
OnDblClick;
OnDestroy;
OnDockDrop;
OnDockOver;
OnDragDrop;
OnDragOver;
OnEndDock;
OnEndDrag;
OnEnter;
OnHelp;
OnHide;
OnHint;
OnIdle;
OnKeyDown;
OnKeyPress;
OnKeyUp;
OnMouseDown;
OnMouseMove;
property
property
property
property
property
property
property
property
property
property
OnMouseUp;
OnMoved;
OnPaint;
OnResize;
OnScroll;
OnShow;
OnShowHint;
OnStartDock;
OnStartDrag;
OnUnDock;
La compatibilité de ces évènements dépendra de quelle classe la dérivation a eu lieu.
VI) Créer ses propres évènements
C'est possible et le code suivant a pu le montrer :
http://www.delphifr.com/code.aspx?ID=20705
Avant de déclarer votre classe, vous déclarez une choses qu'on a pas encore fait :
type
TEvenementPerso = procedure (Sender:TObject ; Param1:integer ;
Param2:string) of object;
TMonCompo = class [...]
C'est forcément un procedure of object. Le fait de noter of object annonce à Delphi
que ce sera un évènement personnalisé. Pour déclarer l'évènement, il suffit de compléter la
classe avec les éléments nouveaux positionnés de cette manière :
type
TEvenementPerso = procedure (Sender:TObject ; Param1:integer ;
Param2:string) of object;
TMonCompo = class(TGraphicControl)
private
FOnEvent : TEvenementPerso;
published
property OnEvenementPersonnel:TEvenementPerso read FOnEvent write
FOnEvent;
end;
[...]
constructor [...] ;
begin
[...]
FEvent:=nil;
end;
Tout évènement est initialisé à NIL. En anglais, ça signifie "RIEN". Eh bien, FOnEvent n'est
rien, tout en étant quelque chose. Disons plutôt que c'est une valeur spéciale qui dit qu'il n'y a
rien au bout du fil.
Pour appeler l'évènement, il faut tester (avec ASSIGNED obligatoirement) avant de lancer.
Au moment désiré, faites :
if Assigned(FOnEvent) then FOnEvent(Self,0,'chaîne idiote');
Il est d'usage de toujours mettre un SENDER dans les évènements personnels (tous même),
car ça permet souvent de grouper des procédures en une seule (c'est souvent plus lisible).
La variable spéciale SELF renvoie l'objet dans lequel on développe. Attention: SELF sert
uniquement pour les composants.
Remarquez : TNotifyEvent = procedure(Sender:TObject) of object; (c'est ce
qui a été utilisé dans http://www.delphifr.com/code.aspx?ID=20705)
VII) Intercepter les messages
Ce paragraphe concerne uniquement les composants fenêtrés TWinControl et
TCustomControl.
Important : Déclarez l'unité MESSAGES dans la clause USES.
Pour intercepter un message, structurez les éléments comme cela :
uses [...], Messages;
type
TMonCompo = class(TCustomControl)
private
procedure WMSize(var Message: TWMSize); message WM_SIZE;
end;
implementation
procedure TMonCompo.WMSize(var Message: TWMSize);
begin
inherited;
end;
end.
Le nom de la procedure et le type de la variable Message est directement fonction du nom du
message à intercepter. Si le type n'est pas reconnu, utilisez TMessage. TMessage marche
dans tous les cas, mais le type dédié permet d'accéder à des propriétés avancées.
VIII) Logiciel de création de composants
Je vous propose d'utiliser un logiciel pour éviter de saisir inutilement des dizaines de lignes
de code. Vous pouvez le trouver à l'adresse suivante :
http://altert.family.free.fr/fils/prgms/progd11.html
http://altert.family.free.fr/fils/prgms/exes/dlphcmps.zip
Il fonctionne sous 98 et XP, donc sur le reste des versions (normalement). Malgré son titre, il
ne fait que créer une structure de base. Vous n'aurez rien de miraculeux avec. Vous
gagnerez seulement beaucoup de temps.
C'est moi qui l'ai développé pour me faciliter la vie, et la votre dans le même temps. Vous
dézippez tout dans le dossier de votre choix et lancez l'application (complètement propre,
càd que vous pourrez la supprimer et ne laissera aucune trace REG ni INI). Choisissez la
version de Delphi qui compilera le composant (une fenêtre s'affiche uniquement si plusieurs
versions ont été détectées).
ONGLET GENERAL
1) Donnez le nom de l'unité qui sera créée
2) Donnez la classe de dérivation (parmi les 4 que je vous ai présenté)
3) Choisir la méthode de rafraîchissement (Refresh est correct)
4) Le nom du composant TBidule
5) Donnez la palette de référencement
Cliquez sur la flèche à côté de générer.
ONGLET RESSOURCES
1) Sélectionnez les unités à utiliser.
Suivant
ONGLET CONSTANTES
1) Elles servent pour qualifier des données générales au projet. Le type sera choisi
automatiquement par Delphi.
Pour ajouter plus de 4 constantes, remplissez TOUT le tableau, mettez vous sur la dernière
case et appuyez sur la flèche du bas de votre clavier.
Suivant
ONGLET VARIABLES UNIVERSELLES
1) Elles devront être initialisées.
Suivant
ONGLET EVENEMENT
1) Il suffit de cliquer, et vous verrez bien s'ils sont supportés par la classe de dérivation.
Suivant
ONGLET PROPRIETES HERITEES
1) Idem
Suivant
ONGLET PROPRIETES PERSONNELLES
1) Les colonnes correspondent à la syntaxe suivante. Il est bien sûr conseillé d'avoir une idée
poussée des propriétés dont on aura besoin.
property [NOM] : [TYPE] read [F*] write Set[NOM];
[...]
procedure [CLASSE].Set[NOM] ( Value : [TYPE] );
begin
if Value<>[F*] then
begin
[F*]:=Value;
[METHODE DE RAFRAICHISSEMENT];
end;
end;
Suivant
ONGLET STYLE
1) On n'en a pas parlé, mais il faut laisser les coches par défaut et activer csOPAQUE !!
Suivant
ONGLET OPTIONS COMPLEMENTAIRES
1) C'est d'un niveau bien plus fort. Certaines coches sont dédiées au composants fenêtres.
Il n'y a pas de suivant.
FINAL
1) Cliquez sur "Générer"
Le composant est presque prêt. Il suffit de la compiler et de corriger les erreurs (à force de
cliquer, il y en a forcément).
IX) Conclusion
Comme pour toute chose, il faut avoir de la pratique. Théoriquement, vous avez tout pour
créer des composants. Reste ensuite le délicat problème de comprendre le Windows : les
messages (important !), les fonctions de Kernel, de User et de GDI. La rubrique Composants
de DelphiFr.com propose de bon compos et vous saurez vite en créer, car ça simplifie
beaucoup les choses.
Copyright
© http://altert.family.free.fr/
Ce document est aussi hébergé sur :
http://www.codes-sources.com/