Developpez.com - C
X

Choisissez d'abord la catégorieensuite la rubrique :



Création d'un composant C++ Builder

Par CGi

Le 3 septembre 2002
modifié le 8 juin 2003



1 - Création d'un composant pas à pas :


Dans ce tutoriel nous allons voir comment créer un composant. Nous créerons pour l'exemple un TLabelDate, composant qui affichera la date du jour soit en chiffres soit en lettres.

Création du composant :

Sur le menu faire "Composant" puis "Nouveau composant..." ce qui a pour effet d'ouvrir la boîte de dialogue "Nouveau composant".



Nous allons remplir ses champs :

Type ancêtre : Nous choisirons TCustomLabel car notre composant aura l'aspect d'un Label. On aurait pu dériver notre composant de TLabel, mais les composants ont un inconvénient, c'est qu'on ne peut plus cacher des propriétés qui ont été déclarée __published. Donc si l'on veut que certaines propriétés ne soient pas visibles, on le dérive d'une classe inférieure dans la hiérarchie où les propriétés sont en majorité protected donc invisibles. Il suffira de déclarer __published celles qu'on veut rendre visibles. Ces classes où la majorité des propriétés sont cachées ont dans leur nom la mention Custom. Il faut bien choisir le composant que l'on va dériver en fonction du résultat que l'on veut obtenir. Tout composant BCB a comme ancêtre primaire TComponent, donc nous dériverons nos composants d'un descendant de TComponent. Les particularités d'un composant sont de pouvoir apparaître sur la palette des composants, d'être manipulé à la conception, de posséder et de gérer d'autres composants.

Nom de la classe : Ce champ est rempli automatiquement par "TCustomLabel1", mais il est préférable de lui donner un nom plus intuitif car c'est sous ce nom que vous l'utiliserez plus tard, quand il sera terminé. Nous l'appellerons donc TLabelDate.

Page de la palette : Choisir une page existante ou éditer un nouveau nom ce qui aura pour effet de créer un nouvel onglet. Dans notre exemple le nom est "MesCompo", mais vous êtes libre de lui donner un autre nom.

Nom d'unité : C'est le nom qui sera donné à votre fichier source. Cliquer sur le bouton <...> à droite, sélectionner le dossier où vous désirez le sauvegarder, laisser le nom du .cpp créé automatiquement "LabelDate.cpp" faire enregistrer. Pour ne pas m'y perdre, je me suis créé un dossier nommé "Compo" pour ranger les sources de mes composants.

Le champ chemin se met à jour automatiquement.( Laisser tel quel) Faire "Ok".

Maintenant nous avons le code source minimum pour notre composant, composé de 2 fichiers : "LabelDate.cpp" et "LabelDate.h", avec le nom de sa classe comme on l'avait précédemment saisi. On peut remarquer aussi cette ligne de code : RegisterComponents("MesCompo", classes, 0); qui référence l'onglet ou sera installé notre composant.

Maintenant il nous faut saisir le code pour atteindre notre but. En fait c'est très semblable à la création d'une classe avec la particularité qu'un composant peut avoir des propriétés, déclaré avec le mot réservé __property. Ce qui est pratique avec les propriétés c'est que nous y avons accès à la conception dans l'inspecteur d'objet et leurs manipulations dans le code est semblable à des données membres déclarées public.

Reprenons notre exemple, fichier "LabelDate.h" :
Partie private : nous déclarons un bool FLettres c'est la donnée membre qui mémorisera la nouvelle propriété qui nous indiquera si le LabelDate sera écris en lettres ou en chiffres. Par convention on met la lettre F au début du nom d'une donnée membre qui correspond à une propriété.
Partie protected : nous y déclarons une méthode SetLettres c'est elle qui modifiera la variable FLettre. Puis la méthode à redéfinir GetLabelText c'est elle qui retourne le texte à afficher. C'est une méthode typique à TCustomLabel. (Pour dessiner sur les composants graphiques c'est plutôt la méthode Paint que l'on redéfinit.)
Partie published : c'est là que nous déclarons les propriétés que l'on veut rendre visible. Pour les propriétés héritées que l'on veut rendre visible, il suffit de les déclarer published ex: __property Color; pour les nouvelles propriétés il faut aussi renseigner les méthodes qui permettent de les lire et de les écrire. Dans l'exemple la lecture se fera directement avec la méthode read qui nous retourne FLettre, pour l'écriture nous passerons par la méthode SetLettres que l'on affecte donc à write.

#ifndef LabelDateH
#define LabelDateH
//---------------------------------------------------------------------------
#include <SysUtils.hpp>
#include <Controls.hpp>
#include <Classes.hpp>
#include <StdCtrls.hpp>
//---------------------------------------------------------------------------
class PACKAGE TLabelDate : public TCustomLabel
{
private:
        bool FLettres; // Variable qui mémorise la nouvelle propriété.
protected:
        void __fastcall SetLettres ( bool ALettres);
        // SetLettres est la fonction qui modifie la nouvelle propriété.
        AnsiString __fastcall GetLabelText();
public:
        __fastcall TLabelDate(TComponent* Owner);
__published:
        // Pour les propriétés descendant de TCustomLabel que l'on veut rendre
        // visible : il suffit juste de les déclarer comme si dessous :
        __property Align;
        __property Color;
        __property Font;
        __property Alignment;
        __property Layout;
        __property Anchors;
        __property AutoSize;
        __property Transparent;
        // Evénement descendant de TCustomLabel :
        __property OnClick;
        // Nouvelle propriété
        __property bool Lettres = {read=FLettres, write=SetLettres, default=false};

};
//---------------------------------------------------------------------------
#endif

Dans le fichier "LabelDate.cpp" il y a juste à rajouter l'implémentation des 2 méthodes SetLettres et GetLabelText : SetLettres affecte à FLettres sa nouvelle valeur puis appelle Invalidate pour afficher cette nouvelle valeur puis appelle AdjustBounds pour ajuster la taille du label si AutoSize est à true. La redéfinition de "GetLabelText" retourne le texte à afficher en fonction de la valeur de FLettres (GetLabelText est appelée automatiquement par la méthode Paint.)

#include <vcl.h>
#pragma hdrstop

#include "LabelDate.h"

#pragma package(smart_init)
//---------------------------------------------------------------------------
// ValidCtrCheck est utilisé pour vérifier que les composants créés n'ont
// aucune fonction virtuelle pure
//
static inline void ValidCtrCheck(TLabelDate *)
{
        new TLabelDate(NULL);
}
//---------------------------------------------------------------------------
__fastcall TLabelDate::TLabelDate(TComponent* Owner)
        : TCustomLabel(Owner)
{
}
//---------------------------------------------------------------------------
//------ SetLettres modifie la variable FLettres qui est la valeur
//-------renvoyée par la propriété
void __fastcall TLabelDate::SetLettres( bool ALettres )
{
        if (ALettres != FLettres)
                {
                 FLettres = ALettres;
                 Invalidate();
                 AdjustBounds();
                }
}
//---------------------------------------------------------------------------
//----Redéfinition de la fonction GetLabelText
AnsiString __fastcall TLabelDate::GetLabelText()
{
   if (FLettres)  return FormatDateTime("dddd d mmmm yyyy" , Date());
   else  return FormatDateTime("d/m/yyyy" , Date());
}
//---------------------------------------------------------------------------
//-Enregistrement du composant et affichage de son icône dans la palette.
//- ici il est mis dans l'onglet "MesCompo"
namespace Labeldate
{
        void __fastcall PACKAGE Register()
        {
                 TComponentClass classes[1] = {__classid(TLabelDate)};
                 RegisterComponents("MesCompo", classes, 0);
        }
}

Il nous reste plus qu'à sauvegarder nos fichiers. A ce stade notre composant n'est toujours pas visible dans la palette, donc il nous faut l'installer. Ce que nous allons voir dans l'étape suivante.




Création du paquet :

Si c'est votre premier composant, il serait préférable de créer un nouveau paquet. Un paquet est en fait un conteneur de composants. Donc sur le menu faire "Fichier" puis "Nouveau" puis "Autres...", ce qui nous donne une boîte de dialogue "Nouveaux éléments". Sur l'onglet "Nouveau" choisir l'icône "Paquet", ce qui nous donne la fenêtre de gestion de notre paquet.

L'enregistrer immédiatement sous un nom explicite, par exemple "Pack1.bpk", dans un dossier de votre choix. J'ai personnellement créé un dossier nommé "Paquets" pour enregistrer mes paquets. (Les fichiers bpk sont en fait des makefiles pour construire les paquets.)
Maintenant il nous faut lui ajouter notre composant par la commande "Ajouter" de la fenêtre. Ce qui nous donne une autre boîte de dialogue "Ajout", sur son onglet "Ajout d'unité" faire en face de "Nom d'unité" : "Parcourir..." sélectionner le fichier source de notre composant ".cpp" et faire "Ouvrir". Le fichier source apparaît à l'écran, et il est rajouté à la liste du paquet. Vous pouvez maintenant compiler votre paquet, icône "Compiler" sur la boîte de dialogue du paquet. Puis l'installer, icône "Installer" si cela ne s'est pas fait automatiquement. Sauvegarder votre paquet.
A partir de maintenant votre composant doit être dans la palette et peut être incorporé sur une fiche.
Si vous voulez modifier votre composant il suffit de rouvrir le paquet puis le source du composant dans la liste du paquet. Dans un paquet vous pouvez rajouter autant de composants que vous voulez. Si vous voulez supprimer un composant il faut le supprimer de la liste du paquet et recompiler. Il peut arriver dans certaines circonstances que votre composant soit toujours présent, alors il faut faire la compilation du paquet par le menu "Projet" puis "Construire (paquet)". Comme nous avons procédé l'icône du composant sur la palette est l'icône par défaut attribé par l'EDI. Nous allons donc maintenant voir comment lui donner une icône personnalisée.




Icône représentant le composant sur la palette de composants :

Cette icône est mise dans un fichier ressource spécial qui a comme extension ".DCR" ce fichier doit être dans le même dossier que les fichiers sources du composant avant compilation. Il faut aussi qu'il soit ajouté à la liste du paquet avant compilation. (Il est ajouté automatiquement s'il est présent dans le dossier du composant quand on ajoute le source du composant au paquet.) Il est très simple de les créer avec "Editeur d'images" par "Fichier", "Nouveau...", "Ressources composants (*.dcr)".
Particularités de ce fichier : Le nom du fichier doit être le même que le nom du fichier source du composant. (si "Compo.cpp" alors "Compo.dcr") L'icône doit être dans un Bitmap de taille 24 x 24. Le nom de ce Bitmap doit être le même que celui de la classe du composant en majuscules (si "TCompo" alors "TCOMPO").




Organisation des composants sur la palette :

Vous pouvez organiser vos composants sur la palette. Pour cela faite un click droit sur la palette puis "Propriétés", ce qui nous donne la boîte de dialogue "Propriétés de la palette"

Sur cette boîte de dialogue vous avez deux listes, celle de gauche représente les onglets et celle de droite les composants. Vous pouvez les faire glisser, déposer pour les changer d'onglet. Vous pouvez les cacher, mais dans ce cas il n'est pas très intuitif de les retrouver. Pour cela il faut sélectionner l'option "[Toutes]" dans la liste d'onglets, là vous pourrez enfin le voir dans la liste des composants. Un composant qui a été déplacé sur la palette garde son emplacement même s'il est recompilé avec indication d'un autre emplacement.

Conclusion: Je vous ai donné la procédure qui me semble la plus adaptée pour faire des composants. Il y a plusieurs façons de faire, quand vous aurez pratiqué un peu la conception de composants vous vous apercevrez que ce n'est pas aussi compliqué qu'on pourrait le croire.



Bonne composition.
CGi


Avec la contribution d'Alacazam pour la relecture.

Télécharger les sources.
(Est inclus le fichier "Labeldate.dcr" contenant l'icône qui le représente sur la palette de composants)





Sommaire

1 - Création d'un composant pas à pas.
2 - Création d'un composant graphique.
3 - Propriété de type TStrings.
4 - Propriété de type TBitmap et contrôle du Owner.
5 - Propriété de type enum.
6 - Contrôle fenêtré.
7 - Evénement personnalisé et sous-propriétés.
8 - Composant pouvant interagir avec ses semblables.
9 - Ressources dans un composant.
10 - Boîte de dialogue dans un composant.
11 - Composant conteneur.
12 - Les éditeurs de composants. Nouveau



C/C++
  Les pointeurs du C/C++.   Les listes chaînées.             Liste simple.             Liste triée.             Liste double.   Les arbres.   Les tas.   Le C orienté objets ?

  1 - La fenêtre principale.   2 - Contrôles et messages.   3 - Les commandes.   4 - Dialogue std.   5 - Contexte de périph.   6 - Dessiner.   7 - Les ressources.   8 - Dialogue perso.   9 - Dialogue comm.   10 - Les accélérateurs.

Assembleur
  Assembleur sous Visual C++.

C++ BUILDER
  Trucs et astuces.   Composant.   TRichEdit.   TDrawGrid.   Application MDI.   TThread.   wxWidgets.   Style Win XP.

  Première application.   Construire un menu.   Dessiner.   Sisers, Timers...   Dialogues standards.   Dialogues perso.

DotNet
  Composant C# Builder.   Contrôle WinForm.   Application MDI.

Java
  Applet java.





Copyright 2002-2016 CGi - Tous droits réservés CGi. Toutes reproduction, utilisation ou diffusion de ce document par quelque moyen que ce soit autre que pour un usage personnel doit faire l'objet d'une autorisation écrite de la part de l'auteur, propriétaire des droits intellectuels.
Les codes sources de ce document sont fournis en l'état. L'utilisateur les utilise à ses risques et périls, sans garantie d'aucune sorte de la part de l'auteur. L'auteur n'est responsable d'aucun dommage subi par l'utilisateur pouvant résulter de l'utilisation ou de la distribution des codes sources de ce document.
De la même façon, l'auteur n'est en aucun cas responsable d'une quelconque perte de revenus ou de profits, ou de données, ou de tous dommages directs ou indirects, susceptibles de survenir du fait de l'utilisation des codes sources de ce document, quand bien même l'auteur aurait été averti de la possibilité de tels dommages. L'utilisation des codes sources de ce document vaut acceptation par l'utilisateur des termes de la licence ci-dessus.

Contacter le responsable de la rubrique C