Tutoriel sur la création des éditeurs de property

Ce document présente un exemple de développement d'un éditeur de propriété, pour les composants de la VCL sous C++Builder.

Article lu   fois.

L'auteur

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

Introduction

Nous allons voir comment ajouter un éditeur de propriété ou property de type source de donnée, à un contrôle Tlabel. Comment créer deux paquets, un pour le Run-Time et un autre pour le Design-Time (IDE). Une fois installé, le composant permet la selection d'un DataSet, ainsi que la selection d'un Champs de la table

Image non disponible
Selection de la source de donnée


Image non disponible
Selection du champ de la table selectionnée

Il permet donc d'afficher le contenu d'un champs d'une table, dans une zone de text static, vous pouvez l'utiliser par exemple pour afficher la date de création d'une fiche, etc..
Bien sûr un composant du même type, existe déjà dans C++Builder. Le but de cet article, est de vous présenter comment ajouter un tel éditeur de propriétés à un composant Maison. C'est un premier pas, vers la création de composants plus compliqués. Nous allons écrire du code simple, mais qui présente, une démarche standard et fournit des class et méthodes que vous pourrez réutiliser facilement.

1. On construit le paquet de [Run-Time] en premier

Sélectionnez dans le menu fichier, nouveau, Autre sélectionnez l'icône Paquet et cliquez sur le bouton OK.

Image non disponible

C++Builder, ajoute un nouveau Paquet, Cliquez sur l'icône Options de votre nouveau paquet

Image non disponible

Sélectionnez l'onglet Description, dans Option d'utilisation, selectionnez Seulement en execution
Enregistrez le paquet sous le nom DVPDBLabelRT

1.1. Ajoutez un control [Tlabel]

Cliquez sur l'icone Ajouter
Sélectionnez l'onglet Nouveau composant
Déroulez le Combo Type ancêtre et sélectionnez Tlabel[StdCtrls]
Dans Page de palette entrez DVP-DVSoft
Cliquez sur le bouton .. qui est à droite du champ Nom d'unité
Indiquez le chemin d'accès et le nom de votre nouveau composant Validez
Cliquez sur le bouton Ok

Image non disponible

1.2. Modifiez la class du nouveau composant

Pour permettre la selection d'une source de donnée et d'un champs de la base, ajoutez à la class les propriétés suivantes.

Type Nom Description
TDataSource FDataSource Utiliser pour la liaison de donnée
AnsiString FFieldName Utiliser pour le stockage du nom du champs
TField* FField Accés à l'object Field de la source de données


Les méthodes private pour accéder aux propriétés de la class
Un Destructeur pour supprimer le DataSource à la destuction du composant
Les __published __property pour accéder aux propriétés de la class

 
Sélectionnez
//---------------------------------------------------------------------------
class PACKAGE TDVPDBLabel : public TLabel
{
private:
    Db::TDataSource* FDataSource;    // Source de donnée
    AnsiString       FFieldName;     // Nom du Champs
    Db::TField*      FField;         // Pointeur sur le champ
    //--- Methode d'accées aux property
    void             __fastcall SetDataSource(Db::TDataSource* Src);
    Db::TDataSource* __fastcall GetDataSource(void);
    void             __fastcall SetFieldName(const AnsiString Value);
protected:
public:
    __fastcall TDVPDBLabel(TComponent* Owner);
    __fastcall ~TDVPDBLabel();    
    //--- Methode
    virtual void __fastcall Assign(Classes::TPersistent* Source);
__published:
    __property Db::TDataSource* DataSource={read=GetDataSource,write=SetDataSource};
    __property AnsiString       FieldName={read=FFieldName, write=SetFieldName};
};

1.3. Codez les méthodes de la class

Accés à la property Datasource

 
Sélectionnez
void __fastcall TDVPDBLabel::SetDataSource(Db::TDataSource* Src)
{
    //--- Seulement si changement de Table source
    if (Src != FDataSource) {
        FDataSource = Src;
        if (Src)
            //--- Ne pas oublier !!!
            Src->FreeNotification(this);
    }// End IF
}
//---------------------------------------------------------------------------
Db::TDataSource* __fastcall TDVPDBLabel::GetDataSource(void)
{
    //--- Pointeur sur l'object DataSource
    return FDataSource;
}

Accés à la property FieldName

 
Sélectionnez
//---------------------------------------------------------------------------
void __fastcall TDVPDBLabel::SetFieldName(const AnsiString Value)
{
    //--- Nom du Champ
    FFieldName = Value;
    //
    // Vérifier que la Chaine Value n'est pas vide 
    // et initialiser le Pointeur sur L'object champs
    //
    if (!Value.IsEmpty() && FDataSource)
        FField = FDataSource->DataSet->FieldByName(Value);
    else
        FField = NULL;
}

Constructeur, destructeur et methode assign pour la copie depuis le flux de chargement

 
Sélectionnez
//---------------------------------------------------------------------------
__fastcall TDVPDBLabel::TDVPDBLabel(TComponent* Owner)
    : TLabel(Owner)
{
    //
    // Creation du lien de donnée
    //
    FDataSource = new Db::TDataSource(NULL);
    //
    // Initialisation du Bouton
    //
    FField     = NULL;
    FFieldName = "";
}
//---------------------------------------------------------------------------
__fastcall TDVPDBLabel::~TDVPDBLabel()
{
    //
    // DataSource CleanUp
    //
    delete FDataSource;
}
//---------------------------------------------------------------------------
void __fastcall TDVPDBLabel::Assign(Classes::TPersistent* Source)
{
    //
    // Copie des Champ de la Class
    //
    FDataSource = ((TDVPDBLabel*)Source)->FDataSource;
    FField      = ((TDVPDBLabel*)Source)->FField;
    FFieldName  = ((TDVPDBLabel*)Source)->FFieldName;
}

Vous n'avez plus qu'a compiler votre paquet.

Attention ne cliquez pas sur installer, c'est juste un paquet d'exécution

Sélectionnez dans le menu fichier, Tous fermer

2. Création du paquet de [Design-Time]

Pour le paquet design Time nous allons construire un nouveau paquet, ajouter les methodes, qui permettent à l'inspeceur d'objets de l'IDE, d'accéder aux property de notre composant permettre la sélection de la source de donnée, et produire la liste des champs de la table. Construire une class, pour enregistrer l'éditeur de property dans l'IDE.

2.1. Nouveau paquet

Créer un nouveau paquet, comme pour la construction du paquet Run-Time, enregistrez le sous le nom DVPDBLabelDE. Dans Option d'utilisation selectionnez Seulement en conception

Image non disponible

2.2. Ajouter le paquet [designide]

Pour que notre paquet de designe soit pris en compte par l'IDE, il faut lui ajouter le paquet designide.bpi ainsi que le paquet RunTime de notre composant DVPDBLabelRT. Selectionnez la section Requires du paquet et cliquez sur Ajouter.

Image non disponible


Cliquez sur le bouton Parcourir, dans votre repertoire d'installation de C++Builder déplacez vous dans le repertoire lib et sélectionnez le fichier designide.bpi

Image non disponible


Effectuer la même opération, pour ajouter votre paquet DVPDBLabelRT.

2.3. Ajouter l'unité de gestion des propriétés

Pour coder la gestion des propriétés, nous allons ajouter une nouvelle unité au paquet. Dans le menu fichier sélectionnez Nouveau, unité. Enregistrez le fichier dans le répertoire des sources du paquet, sous le nom TDVPDBLabelDEReg. Une fois ces opérations effectuées, votre paquet doit ressembler à celui là.

Image non disponible

2.4. Ajoutez la class de gestion des propriétés

Sélectionnez la page Header de l'unité, puis ajoutez le code suivant.

 
Sélectionnez
//---------------------------------------------------------------------------
// Class utilitaire pour la selection des champs dans l'editeur de propriete
// de l'IDE  [TDVSEnumStrDBButton]
//
class PACKAGE TDataFieldsProperty : public TStringProperty
{
public:
    virtual Designintf::TPropertyAttributes __fastcall GetAttributes(void);
    virtual void __fastcall GetValues(Classes::TGetStrProc Proc);
public:
    __fastcall TDataFieldsProperty(const Designintf::_di_IDesigner ADesigner, int APropCount);
    __fastcall ~TDataFieldsProperty(void);
};

2.5. Ajouter l'utilitaire d'enregistrement de la propriété FieldName

Selectionnez, la page code de l'unité.
La propriété de notre composant, ne peut étre prise en compte par les editeurs de propriétés par défaut de l'IDE, nous allons donc utiliser la methode RegisterPropertyEditor. Pour simplifier l'accès à l'argument PropertyType, nous allons créer une fonction utilitaire.

extern PACKAGE void __fastcall RegisterPropertyEditor( Typinfo::PTypeInfo PropertyType, System::TMetaClass* ComponentClass, constAnsiString PropertyName, System::TMetaClass* EditorClass);


 
Sélectionnez
//---------------------------------------------------------------------------
// Utilitaire d'acces a :Property FieldName
//
PTypeInfo FieldNameTypeInfo(void)
{
    PPTypeInfo Temp;
    //
    // Pour simplifier l'acces a la property AinsiString [FieldName]
    //
    Temp = GetPropInfo(__typeinfo(TDVPDBLabel), "FieldName")->PropType;
    assert(Temp != NULL);
    return *Temp;
}

2.6. Constructeur et destructeur de la class

Rien de particulier, ils peuvent être codés en inline dans la déclaration de la class

 
Sélectionnez
//---------------------------------------------------------------------------
// Class utilitaire pour la selection des champs dans l'editeur de propriete
// de l'IDE  
//
//---------------------------------------------------------------------------
__fastcall TDataFieldsProperty::TDataFieldsProperty(const Designintf::_di_IDesigner ADesigner,
            int APropCount)
            : Designeditors::TStringProperty(ADesigner, APropCount)
{
}
//---------------------------------------------------------------------------
__fastcall TDataFieldsProperty::~TDataFieldsProperty(void)
{
}

2.7. Methodes de la class

L'IDE appelle la méthode GetAttributes pour lire les attributs de la class, dans notre cas, les attributs sont de type Designintf::TPropertyAttributes, c'est une liste de valeur qui est triée.

 
Sélectionnez
//---------------------------------------------------------------------------
Designintf::TPropertyAttributes __fastcall TDataFieldsProperty::GetAttributes(void)
{
    //
    // Attributs de la Property [Liste de champs, triée]
    //
    Designintf::TPropertyAttributes Attr;
    Attr << paValueList << paSortList;
    return Attr;
}

Le méthode GetValues est un peu plus compliquée. Nous devons fournis à l'IDE la liste des champs de la table. Le Flags __DEBUG__ permet d'afficher le résultat des contrôles effectuer pendant les assignations de valeur.
Notre composant est toujours le premier dans la liste, utiliser la mèthode GetComponent(0) pour obtenir un pointeur sur cet Object et accéder à ces property

 
Sélectionnez
//---------------------------------------------------------------------------
void __fastcall TDataFieldsProperty::GetValues(Classes::TGetStrProc Proc)
{
    TDVPDBLabel*  Label;          // Pointeur sur L'object TDVPDBLabel
    TDataSource*  DataSource;     // Pointeur sur la source de donnée
    TDataSet*     DataSet;        // Pointeur sur le descripteur de table
    TStringList*  FieldsList;     // Liste des noms de champs
    //
    // Pointeur sur le composant selectionner [TDVPDBLabel[X]]
    //
    Label = (TDVPDBLabel*)GetComponent(0);
    //
    // Pointeur sur le DataSource du composant Bouton
    //
    DataSource = Label->DataSource;
    if (!DataSource) {
#ifdef __DEBUG__
        MessageDlg("Le Datasource NULL", mtWarning, TMsgDlgButtons() << mbOK, 0);
#endif
        return;
    }// End IF
    //
    // Pointeur sur le DataSet du composant, pour lire les champs disponible
    //
    DataSet = DataSource->DataSet;
    if (!DataSet) {
#ifdef __DEBUG__
        MessageDlg("Le Dataset NULL", mtWarning, TMsgDlgButtons() << mbOK, 0);
#endif
        return;
    }// End If
    //
    // Creer une liste de AnsiString pour stocker les noms de champs
    //
    FieldsList = new TStringList;
    //
    // Demander la liste des champs de la Table
    //
    DataSet->GetFieldNames(FieldsList);
    //
    // Ajouter a l'editeur de property
    // ATTENTION on commence a partire de 1
    //
    for (int i = 1; i <= FieldsList->Count;i++)
        Proc(FieldsList->Strings[i - 1]);
    //
    // CleanUp
    //
    delete FieldsList;
}

2.8. Enregistrement du composant

Il ne nous reste plus qu'à enregistrer le composant et son éditeur de property dans l'IDE.

 
Sélectionnez
//---------------------------------------------------------------------------
namespace Tdvpdblabeldereg
{
    void __fastcall PACKAGE Register()
    {
        TComponentClass classes[1] = {__classid(TDVPDBLabel)};
        //
        // Enregistrement du composant
        //
        RegisterComponents("DVP-DVSoft", classes, 0);
        //
        // Enregistrement de l'editeur de property pour les noms de champs
        // de la property [FieldName] du composant [TDVPDBLabel]
        //
        RegisterPropertyEditor(FieldNameTypeInfo(),              // Notre fonction utilitaire
                                __classid(TDVPDBLabel),          // La class du composant
                                "FieldName",                     // Le nom de la Property
                                __classid(TDataFieldsProperty)); // La class de gestion de l'editeur
    }
}

Voilà, il ne reste plus qu'à compiler et installer notre composant.
Vous devez avoir une nouvelle page DVP-DVSoft dans la liste des composants. Enregistrez et fermez tout.

3. Conclusion

Il n'est pas très compliqué de développer un éditeur de property pour un composant Maison.
A suivre, les Editeurs de property pour les composants TCollection. Avec un composant TDBTreeView qui permet d'afficher le contenu d'une table, dans un TreeView
Un grand merci à ska_root pour sa relecture.

Téléchargement du paquet et des sources

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Copyright © 2005 DVSoft. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.