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
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.
C++Builder, ajoute un nouveau Paquet, Cliquez sur l'icône Options de votre nouveau paquet
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
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
//---------------------------------------------------------------------------
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
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
//---------------------------------------------------------------------------
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
//---------------------------------------------------------------------------
__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
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.
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
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à.
2-4. Ajoutez la class de gestion des propriétés▲
Sélectionnez la page Header de l'unité, puis ajoutez le code suivant.
//---------------------------------------------------------------------------
// 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); |
//---------------------------------------------------------------------------
// 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
//---------------------------------------------------------------------------
// 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.
//---------------------------------------------------------------------------
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
//---------------------------------------------------------------------------
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.
//---------------------------------------------------------------------------
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.