- Remarque : Cette rubrique a été écrite alors que D3 et D4 n'étaient pas encore sortis. La grande difference est que D3 et D4 placent les composants dans des paquets. (Fichier .dpk). Pour lancer l'expert "Création de composant" avec D3 il faut faires composants/Nouveau composant. On obtient un ecran un peut different de celui qui figure un peu plus bas en exemple. Mais les differences ne sont pas énormes. Dès que j'ai un peu de temps je mettrais ce chapitre à jour. Par contre pour le reste tout est valable pour D3 et D4. Bon developpement...
- Introduction :
- On m'a souvent demandé dans mon entrourage pro. quelle etait la marche à suivre pour créer un nouveau composant et le placer dans la palette de la VCL. Je pense donc que cela peu interesser du monde. Aussi j'ai ecris cette page dans le but d'aider un peu les debutants à ecrire leur premier composant. Cependant avant de se lancer dans l'écriture de composants une connaissance minimum de Delphi et du pascal objet est necessaire car ici on ne manipule pas des objets deja existants mais seulement des lignes de codes. Cependant une fois qu'on a compris le principe, la création de composant n'est pas trés difficile. En fait cela est surtout fonction de ce que vous voulez que votre composant soit capable de faire.
- Un peu d'ordre :
- Pour commencer un peu d'ordre. Pour ne pas ce melanger les pinceaux on va commencer par creer un dossier reservé aux composants en cours de developpement. Apres une installation classique les composants fournis avec delphi sont stockés dans le dossier BORLAND/DELPHI.../LIB. Une maniere simple de s'organiser et de créer deux sous dossiers au dossier LIB. Le premier, COMPOSANTS PERSO, contiendra les composants une fois terminés et testés. Le second sous dossier, DEVELOPPEMENT, contiendra les composants en cours de developpement. On pourrait être tenté de placer chaque nouveau composant dans un dossier specifique. Mais Delphi memorise les chemins d'accés de chaque composant et si le nombre de dossier devient trop important Delphi sature et ne peut plus installer de nouveaux composants.
- L'exemple de cette page :
- Nous allons créer un composant simple: Un panel qui changera de couleurs à chaque click. Ce n'est pas trés utile, mais c'est juste le principe de création que je veux expliquer ici. Ce composant d'exemple suffira a voir les points essentiels. Comme le source du composant ne sera pas entierement publié dans cette page vous pouvez le telecharger. Avec le source sous les yeux les explications seront encore plus claires.
- Par ou commencer :
- Un composant est toujours contenu dans une unité Delphi. (Fichier *.PAS ou *.DCU si le fichier est deja compilé). Il peut y avoir plusieurs composants dans une unité. Mais cela n'est pas trop recommandé car si le source est grand on finit toujours par se perdre entre les differents composants de l'unité. Ou alors il faut utiliser la directive d'inclusion de fichier $I mais cela multiplie les fichiers.
- En premier il faut choisir quel composant ou objet sera l'ancêtre de notre nouveau composant. Comme on ne peut pas retrancher de proprieté lors du processus d'heritage il ne faut pas que le composant ancêtre en possede une dont on ne veut pas dans le composant final. Dans notre exemple le mieux serait d'utiliser TPanel comme composant ancêtre. Mais pour pouvoir expliquer comment rendre visible une proprieté cachée de l'ancêtre nous utiliserons TCustomPanel. TPanel est un descendant direct de TCustomPanel et notre composant le sera aussi.
- Bien qu'il soit possible de créer de toute piece l'unité contenant le composant une autre solution plus simple existe :
L'expert creation de composant. Pour l'activer faire Composants/Installer.
Remplissez les trois Edits comme ci dessus puis validez. On obtient ce qui suit, c'est à dire l'ossature de l'unité.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ExtCtrls;
type
TColorPanel = class(TCustomPanel)
private
{ Déclarations privées }
protected
{ Déclarations protégées }
public
{ Déclarations publiques }
published
{ Déclarations publiées }
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Exemples', [TColorPanel]);
end;
end.
- Premiere chose à faire : Donner un nom à l'unité. Faire enregistrer sous et suvegarder l'unité sous le nom UColorPanel dans le dossier developpement. La premiere ligne de l'unité doit devenir Unit UColorPanel; Prenez l'habitude de faire commencer les noms d'unité par U. Ce n'est pas obligatoire mais c'est plus clair lorsqu'on visualise le contenu d'un dossier.
- Si on regarde d'un peu plus près le texte de l'unité on voit que l'expert composant placé une procedure registers pour enregistrer le composant sur l'onglet Exemples, que le nouveau composant est bien TColorPanel et qu'il descend bien de TCustomPanel. De plus l'expert a placé une clause Uses contenant les unités les plus courantes. Si votre nouveau composant à besoin d'une unité speciale c'est là qu'il faudra la rajouter.
- Et ensuite ?
- Deux choses imperatives: Le constructeur et de destructeur du composant.
- Par convention les constructeurs et destructeurs sont toujours public, de plus ils surchargent des methodes virtuelles donc dans la declaration de type du composant section Public on declare :
Constructor Create(AOwner:TComponent); Override;
Destructor Destroy; Override;
Dans la section Implementation de l'unité on ecrit :
Constructor TColorPanel.Create(AOwner:TComponent);
Begin
Inherited Create(AOwner); // On appele toujours le construteur herité
{ Si des initialisations doivent être effectuées (Variables ou Proprietés...)
on place le code ici }
end;
Destructor TColorPanel.Destroy;
Begin
{ Si le composant doit liberer des ressources
on place le code ici }
Inherited Destroy; // On appele toujours le destructeur herité
end;
- Ensuite il ne reste plus qu'a ecrire le code de votre composant. C'est ce qu'on va voir maintenant en examinant le cas des variables, des procédures et fonctions, des méthodes, des proprietés et enfin des évènements.
- Choisissez la rubrique qui vous interesse :
Les variables.
Les procedures et les fonctions.
Les methodes.
Les proprietés.
Les evenements.
Fichier d'aide et représentation graphique du composant.
Installation du composant sur la palette.
Retour au debut du tutorial.
- Les variables :
L'utilisation des variables dans un composant fonctionne de la même maniere que dans un projet classique. Cependant si on veut respecter les regles de base de la programmation objet il ne faut pas déclarer de variables Public. Si vous devez rendre accessible une variable à l'application qui utilise votre composant il faudra utiliser une proprieté pour acceder à votre variable, qui elle sera declarée Private.
Voyons un exemple : Vous avez declaré public la variable suivante MaVariable:Byte. Votre application accede à cette variable de la façon suivante MonComposant.MaVariable:=50; et cela fonctionne. Mais lors d'une mise à jour de votre composant vous voulez que lorsqu'on ecrit une valeur<125 dans MaVariable votre composant devienne bleu et rouge si MaVariable est > 125. Vous voulez bien sur que le changement intervienne dès l'ecriture dans MaVariable et non apres une quelconque manip. De plus il faut que la nouvelle implementation de MonComposant soit compatible avec l'ancienne afin de ne pas être oblige de modifier l'application utilisatrice. Il n'y a pas de solution élégante. Par contre avec l'utilisation d'une proprieté le probleme serait vite resolu car une proprieté peut appeler une procedure lorsqu'elle est modifiée.
Vous pouvez aussi declarer une variable Protected. Elle ne sera visible qu'a l'interieur de votre composant mais elle poura être redeclarée public par les descendants mais dans ce cas on retombe sur le problème ce dessus. Donc, il est plus elegant de declarer une proprieté Protegées qu'une variable.
Il n'y a pas de variable Published. Cette section possede les même caracterisque que la section Public mais en plus ce qui y figure est en relation avec l'inspecteur d'objet.
- Les procedures et fonctions :
La aussi tout est similaire au fonctionnement des procedures et fonctions dans un projet classique.
Les procedures et fonctions Private ne seront visibles qu'a l'interieur de votre composant. Tandis que si vous declarez vos procedures Protected elles seront visibles par les descendants du composant. Il y a peu de fonctions Public car dans ce cas une proprieté est souvent plus judicieuse. Ainsi lorsqu'on cré une procedure Public (Une methode) il faut reflechir si une proprieté ne pourrait pas remplacer la methode. Surtout si la methode n'a qu'un paramètre. (Une proprieté peut appeler une procedure lorsqu'elle est modifiée.)
- Les methodes :
Les methodes sont des procedures Public. Elles ne sont accessibles que pendant l'execution. Souvent il est possible de definir une proprieté plutot qu'une methode. Par exemple les composants pourraient posseder une methode
SetWidth(Width:Integer) Lors de de l'appel de cette methode la largeur serait memorisée dans une variable private du composant puis redessiné avec la nouvelle largeur par le methode SetWidth. Mais comme une proprieté peut appeler une procedure c'est une proprieté Width qui a été implementée et qui appele une procedure SetWidth lorsqu'elle est modifiée. L'avantage est qu'une proprieté Published est accessible pendant la phase de conception du projet dans l'inspecteur d'objet. Ainsi lorsqu'on modifie la proprieté Width on voit tout de suite ce qui se passe.
- Les proprietés :
Les proprietés sont les éléments fondamentals d'un composant. Elles peuvent être :
Public: La proprieté est accessible à l'application pendant l'execution.
Published : Comme public mais en plus la proprieté est accessible dans l'inspecteur d'objet pendant la conception du projet.
Protected : La proprieté n'est pas accessible à l'application mais un descendant du composant pourra la redeclarer Public ou Published
Il serait idiot de declarer une proprieté Private. Dans ce cas une variable suffit.
Bien que pour l'application utilisatrice une propieté se comporte le plus souvent comme une variable cela n'en est pas une. C'est plutot une intermediare entre l'application et une variable Private du composant. Exemple: Dans TColorPanel On va definir une proprieté de type Boolean qui autorisera ou interdira le changement de couleur lors d'un click sur le composant. Section Private on definit la variable comme suit
Private
{... ...}
FColorChange:Boolean; // Les noms de variables associées à une proprieté commencent par F et porte le nom de la proprieté ( C'est une convention pas une obligation)
{... ...}
Puis section Published on ecrit :
Published
{... ...}
Property ColorChange:Boolean Read FColorChange Write FColorChange;
{... ...}
Ce n'est pas plus compliqué, mais on peut faire mieux. Dans l'exemple ci dessus la valeur donnée à la proprieté est directement ecrite dans la variable FColorChange. Mais si on veut que TCColorPanel prenne une couleur particuliere lorqu'on interdit la modification de couleur comment fait on ? On a vu qu'une proprieté peut appeler une procedure lorsqu'elle est modifiée.
Il faut pour cela declarer une procedure privée acceptant un seul parametre du type de la proprieté.
Private
{... ...}
Procedure SetColorChange(AColorChange:Boolean); // Les noms de procedure associées à une ecriture de proprieté commencent par Set et porte le nom de la proprieté ( C'est une convention pas une obligation)
FColorChange:Boolean; // Les noms de variables associées à une proprieté commencent par F et porte le nom de la proprieté ( C'est une convention pas une obligation)
{... ...}
Ensuite dans l'implementation du composant on ecrit la procedure:
Procedure TColorPanel.SetColorChange(AColorChange:Boolean); // La nouvelle valeur de la proprieté est dans AColorChange
Begin
If AColorChange=FColorChange then exit; // Si la valeur de la prorieté na pas été modifié on sort
FColorChange:=AColorChange; // On passe la nouvelle valeur dans la variable associée
If Not FColorChange then Color:=ClBlue; // Couleur par defaut lorsqu'on bascule la proprieté à false
{ On place ici le code à executer lors de la modif de la proprieté }
end;
Et enfin on modifie la definition de la proprieté pour qu'elle appele la procedure lors d'une modification. la nouvelle definition est : ( La partie concernée est souligné)
Property ColorChange:Boolean Read FColorChange Write SetColorChange
De même il est possible d'executer du code lors de la lecture d'une proprieté. Mais dans ce cas il faut definir une fonction renvoyant une valeur du type de la proprieté
Private
{... ...}
Function GetColorChange:Boolean; // Les noms de procedure associées à une lecture de proprieté commencent par Get et porte le nom de la proprieté ( C'est une convention pas une obligation)
FColorChange:Boolean; // Les noms de variables associées à une proprieté commencent par F et porte le nom de la proprieté ( C'est une convention pas une obligation)
{... ...}
Dans l'implementation du composant on ecrit le code de la fonction
Function TColorPanel.GetColorChange:Boolean;// La valeur que prendra la proprieté est la valeur que renvoit la fonction
Begin
{ On place ici le code dont on a besoin }
Result:=FColorChange; // On n'oublie pas de donner une valeur de retour à la fonction
end;
Et enfin on modifie la definition de la procedure pour qu'elle appele la fonction lors d'une lecture. la nouvelle definition est : ( La partie concernée est souligné)
Property ColorChange:Boolean Read GetColorChange Write FColorChange
Bien entendue il est tout à fait possible de definir une proprieté qui appelle une procedure lors de son ecriture et une fonction lors de sa lecture.
Il est egalement possible de definir des proprietés Public en lecture seule ou en ecriture seule. Il sufiit d'omettre la partie concernée. Exemple :
Property UneProprieté:Byte Read FUneProprieté; // Lecture seule
Property UneAutreProprieté:Byte Write FUneAutreProprieté; // Ecriture seule
-Les évenements :
Les évenements sont une partie importante des composants car c'est le moyen le plus classique qu'ils ont pour intervenir sur le deroulement d'une application.
En Delphi les évenements se declarent comme des propriétés. Cependant il s'agit de propriétés un peu speciales. En effet ces propriétés ne sont pas de type "classique" (Byte,Boolean ... etc) mais de type pointeur sur une procedure. Effet avec delphi il est possible de créer un type qui designe une procedure. Pour exemple nous allons doter TColorPanel d'un évènement OnNewColor qui sera declanché lorsque le composant changera de couleur. Cette évènement devra fournir à l'application le nom du composant qui a declanché l'évènement ainsi que la nouvelle couleur du composant. En clair, lorsqu'on fera un double click sur OnNewColor dans l'inspecteur d'objet Delphi mettra en place la procedure suivante:
Procedure MonApplication.ColorPanelNewColor(Sender:TObject;Couleur:TColor);
Begin
End;
Pour commencer nous allons créer un nouveau type designant une procedure. Pour cela dans la section Type de l'unité et avant la déclaration de type du composant on ecrit :
Type
{... ...}
TNewColor=Procedure(Sender:TObject;Couleur:TColor) of Object;
{... ...}
Il faut maintenant, dans le composant, déclarer une variable de type TColorChange. Donc Section Private de la déclaration de classe du composant on ecrit :
Private
{... ...}
FNewColor:TNewColor;
{... ...}
Et enfin dans la section Published on declare la proprieté de cette façon :
Published
{... ...}
Property OnNewColore:TNewColor Read FNewColor Write FNewColor; // Par convention le nom d'évènement commencent par On
{... ...}
Et voila, il ne reste plus qu'à fixer quand le composant doit déclancher l'évènement. Ceci ce fait dans la section implementation
Cela est trés simple. Dans le code de votre composant, à l'endroit ou vous voulez que l'évènement soit déclanché il faut ajouter la ligne suivante:
If assigned(FNewColor) then FNewColor(Self,NouvelleCouleur);
If assigned effectue un test afin de savoir si, dans l'application, un évènement est bien associé à OnNewColor. En effet un composant ne doit pas déclancher d'évènement si dans l'inspecteur d'objet l'évènement en question est vide.
Self correspond au composant lui même et NouvelleCouleur est un parametre de type TColor. Bien sur, les paramétres que vous devrez fournir lors du déclanchement sont fonctions de ce qui a été définie lors de la declaration de type de l'évènement.
- Fichier d'aide et representation du composant sur la palette :
Pour finir completement le travail il faut aussi choisir une representation graphique du composant sur la palette et ecrire une aide. Je ne décrirais pas la creation d'un fichier d'aide ici car je n'ai pas encore tout compris. En fait il semble que WRITE génère des fichiers *.RTF qui ne conviennent pas tout à fait au compilateur d'aide fournit avec delphi. Si quelqu'un a une solution, ma B.A.L. est ouverte à tout message.
Par contre pour la representation graphique du composant sur la palette les choses sont plus simples : Avec l'éditeur d'image de Delphi il faut créer un fichier ressources composant (*.DCR) de même nom que l'unité contenant le composant à representer. Dans notre cas un fichier UColorPanel.DCR. Ce fichier doit contenir un Bitmap de 24*24 Pixels du nom du composant. Ici TColorPanel. C'est ce bitmap qui representera le composant sur la palette. L'aide de l'editeur d'image est precise sur ce point.
Petite precision: Il faut que le fichier *.DCR se trouve dans le même dossier que le composant. Delphi ne génère pas d'erreur s'il ne trouve pas de fichier *.DCR, mais dans ce cas il utilise une représentation par defaut pour le composant.
- Installation :
- Pour plus de facilité Il est preferable d'avoir un dossier reservé aux composants perso. Mais Delphi memorise le chemin d'accès de chaque composant. Il est donc preferable d'éviter de multiplier les dossiers contenant les composants car lorsque le nombre de dossier devient trop important Delphi sature et n'arrive plus à installer de nouveaux composants.
- Les composants Delphi etant stockés pour beaucoup dans le dossier LIB, le plus simple est de créer un sous dossier au dossier LIB, par exemple un sous dossier 'COMPOSANTS PERSO' qui contiendra tout les nouveaux composants installés. En Réorganisant le dossier par nom de fichier, les composants seront classés par ordre alphabetique. Une verification du
contenu du dossier sera ainsi facilitée.
- Important : Apres installation, il est imperatif de ne pas déplacer, renomer les fichiers contenant les composants ainsi que le dossier d'installation des composants. Delphi ne pourrait plus retrouver ces fichiers. Si un changement de dossier doit être effectué, il faut désinstaller les composants concernés puis les réinstaller à partir du nouveau dossier.
Lancer Deplhi puis faire :
- Composant
- Installer
- Ajouter
- Parcourir => Se placer dans le dossier contenant les composants.
- Selectionner le fichier contenant le composant (Voir note plus bas)
- Ouvrir
- OK
Laisser faire Delphi quelques instants, il placera le composant sur la palette désignée dans le source.
Note : Par defaut la boite de dialogue demande les fichiers sources composants (*.PAS) mais vous pouvez aussi installer des fichiers composants deja compilés (*.DCU). Si vous achetez des composants tout faits il y a de fortes chance que l'éditeur ne livre pas le source mais seulement le fichier compilé. Dans ce cas il faut selectionner fichier DCU dans la boite de dialogue.
- Conclusion :
La création d'un nouveau composant n'est pas trés difficile lorsqu'on a compris le principe. Cependant une bonne connaissance
du pascal objet est bien utile car lors de l'ecriture d'un composant il faut souvent tout créer. Cependant souvent le travail peut être rendu plus facile en choisissant avec soin l'objet ancêtre. De plus il est possible d'utliser un composant deja existant à l'interieur d'un nouveau composant. Si par exemple vous ecrivez un composant du style boite de dialogue, vous n'avez pas besoin de réecrire le code des boutons car vous pouvez placer des SpeedButtons à l'interieur. La seule difficultée est qu'il faudra ecrire le code d'initialisation des proprietés car là, il n'y a pas d'inspecteur d'objet. Sur la page téléchargement le composant TToolBar comporte un bouton de fermeture. Il s'agit d'un SpeedButton.
Je conseil vivement de telecharger le source du composant TColorPanel d'exemple. J'ai placé de nombreux commentaires qui avec les explications de cette page rendront l'ecriture d'un nouveau composant beaucoup plus claire.
Si vous avez des suggestions ou des questions n'hesitez à me contacter
=> Cliquez ici
Retour au debut du tutorial.