Vous ne trouvez pas de réponse à votre problème ? Alors posez la question dans le forum. Souvenez-vous qu'il n'y a jamais de question bête, mais rester dans l'ignorance parce que l'on n'ose pas poser une question, ça c'est une erreur !

[DELPHI] DÉVELOPPER DES COMPOSANTS


Information sur le tutorial

Catégorie :Composants Date de création : 19/07/2005 13:44:55 Vu : 11 264 fois

Note :
9,5 / 10 - par 4 personnes
9,50 / 10

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

Commentaire sur cette source (1)
Ajouter un commentaire et/ou une note

Description

Suite logique à "Développer des classes"

Tutorial

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é au-dessus 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) et nécessite la lecture du tutorial N°86 :
http://www.delphifr.com/tutorial.aspx?ID=86
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. Voir pour cela le tutorial N°78 :
http://www.delphifr.com/tutorial.aspx?ID=78

 

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 OnClick;
property OnContextPopup;
property OnCreate;
property OnDblClick;
property OnDestroy;
property OnDockDrop;
property OnDockOver;
property OnDragDrop;
property OnDragOver;
property OnEndDock;
property OnEndDrag;
property OnEnter;
property OnHelp;
property OnHide;
property OnHint;
property OnIdle;
property OnKeyDown;
property OnKeyPress;
property OnKeyUp;
property OnMouseDown;
property OnMouseMove;
property OnMouseUp;
property OnMoved;
property OnPaint;
property OnResize;
property OnScroll;
property OnShow;
property OnShowHint;
property OnStartDock;
property OnStartDrag;
property 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(T
CustomControl)
  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).

Pour plus de renseignements sur INHERITED, voir par exemple :
http://www.delphifr.com/code.aspx?ID=25416

 

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.

Tanguy ALTERT, http://altert.family.free.fr/

signaler à un administrateur
Commentaire de Forman le 27/05/2006 11:12:00

"C'est forcément un procedure of object. Le fait de noter of object annonce à Delphi que ce sera un évènement personnalisé"

pas tout à fait vrai, "of object" signifie que c'est une méthode d'object et pas une procédure standard. Concrètement, ça signifie pour Delphi que cette valeur occupe 64 bits en mémoire:
-4 octets pour l'instance qui implémente la méthode
-4 octets pour le pointeur vers le code de la méthode
Par exemple, vois le type TMethod:

type
  TMethod = record
    Code, Data: Pointer;
  end;

un TNotifyEvent (ou toute procedure/function of object) est exactement équivalent à un TMethod. L'inspecteur d'objet utilise MethodAddress et MathodName pour afficher quelle méthode publiée de l'objet a été affectée au champ.

Ceci dit c'est vrai que les propriétés de type procédures standards ne peuvent pas être publiées, ça n'aurait dce toute façon  pas grand intérêt.

Ajouter un commentaire



Nos sponsors

Sondage...

CalendriCode

Octobre 2008
LMMJVSD
  12345
6789101112
13141516171819
20212223242526
2728293031  

Consulter la suite du CalendriCode



Développement réalisé par Nicolas SOREL (Nix) avec l'aide de : Cyril DURAND et Emmanuel BAÏSE, Merci à Vincent pour ses précieux conseils
CodeS-SourceS.com© Toute reproduction même partielle est interdite sauf accord écrit du Webmaster
CodeS-SourceS.com© est une marque déposée tous droits réservés
Temps d'éxécution de la page : 0,016 sec

Google Coop CodeS-SourceS Google Coop CodeS-SourceS


Certaines images présentes sur le site (notament certains avatars) sont issues des collections IconShock, donc si vous souhaitez utiliser ces icons vous devez les acheter, ne les copiez pas et ne utilisez pas dans vos sites et applications sans les avoir commandé.