I. Introduction▲
Dès ses débuts, Delphi a voulu se positionner en tant qu'outil RAD (Rapid Application Development). La philosophie du RAD est de pouvoir dessiner l'application avec des composants et des contrôles visuels en définissant leurs propriétés via l'inspecteur d'objets et en essayant d'avoir à coder le moins possible. Le dynamisme est assuré par le couplage fort entre l'UI et la source de données. Par exemple, lorsque je me déplace dans mon ensemble de données, la donnée représentée par mon contrôle se met à jour automatiquement.
Dans ce cadre, Delphi met à la disposition du développeur des contrôles sensibles aux données (data aware) : quand on veut relier un TEdit, un TComboBox ou un TStringGrid à une source de données, on utilise alors, toujours pour rester dans cet esprit RAD, des TDBEdit, des TDBCombobox ou des TDBGrid.
"So far, so good", comme on dit. Le problème est qu'en voulant faciliter le développement, on le contraint à des solutions prêtes à l'emploi : le TDBxyx est forcément un contrôle du genre Txyz, mais relié à une source de données. Et si je veux relier mon Txyz à autre chose qu'à une source de données, à un autre composant, ou à un autre objet par exemple, je ne le pourrai pas aisément.
Pour résoudre ce problème, les concepteurs de Delphi décidèrent de créer LiveBindings.
LiveBindings est un framework permettant de séparer clairement l'interface utilisateur de la logique métier, de rendre le contrôle visuel (TEdit, TComboBox, TStringGrid) indépendant de la source à laquelle il peut être lié.
II. En conception, liaison du contrôle visuel avec un TPrototypeBindSource▲
Le programme de test est présent dans le fichier compressé LiveBindings accompagnant le présent document.
L'objet du TPrototypeBindSource est de créer une source de données virtuelle censée représenter le TObject que nous voulons utiliser par la suite et dont nous pourrons définir le comportement vis-à-vis du composant auquel il sera lié.
Depuis notre concepteur de fiches :
- ajoutons un TPrototypeBindSource. Depuis son inspecteur d'objets et sa propriété FieldDefs (TGeneratorFieldDefs), ajoutons un TGeneratorFieldDef → TGeneratorFieldDefs[0]. Pour ce TGeneratorFieldDefs[0] nouvellement créé, spécifions la valeur FField1 pour sa propriété Name ;
Notez que la valeur de cette propriété FField1 sera le nom d'un champ du TObject, qui remplacera le TPrototypeBindSource.
- ajoutons le contrôle auquel sera lié notre TObject : un TEdit.
Maintenant, concentrons-nous sur la liaison TPrototypeBindSource ↔ TEdit.
Afin d'obtenir une représentation plus claire, nous dessinerons les liaisons que nous aurons décidées pour notre contrôle visuel, en utilisant les graphes de liaison du concepteur LiveBindings.
Depuis le concepteur LiveBindings :
Les composants et leurs propriétés utilisés ici seront :
- Edit1 (TEdit) / propriété : Text ;
- PrototypeBindSource1 (TPrototypeBindSource) / propriétés : FieldDefs, collection de TGeneratorFieldDef ;
- BindingsList1 (TBindingsList) : ce composant n'est qu'un conteneur de Binding, affichable en double-cliquant dessus. Les actions réalisées dans le concepteur LiveBindings ont créé pour nous un contrôle de liaison adapté au type de liaison que nous avons choisi : lien bidirectionnel entre la propriété d'un contrôle (Text) et un champ de notre TPrototypeBindSource (FField1). Le contrôle de liaison contenu, LinkControlToField1, est du type TLinkControlToField ;
- LinkControlToField1 (TLinkControlToField) / propriétés : Control : Edit1 ; DataSource : PrototypeBindSource1 ; FieldName : FField1 ; Direction : LinkBidirectional.
Vous aurez peut-être remarqué qu'à aucun moment du processus de développement via LiveBindings il n'a été question de faire référence à la propriété Text de Edit1, pourtant un des deux points de liaison. La raison en est que le contrôle de liaison utilisé, le TLinkControlToField, est de la catégorie Quick Bindings, et qu'à ce titre le membre enregistré comme observable sera mis en avant dans le concepteur LiveBindings.
Ex. : pour un TEdit, le membre dit observable enregistré est sa propriété Text.
Edit1 n'a aucune connaissance du composant auquel nous voulons le lier, PrototypeBindSource1. De la même façon, PrototypeBindSource1 n'a aucune connaissance du contrôle auquel on veut le lier, Edit1.
Ceci est primordial, car cela montre que l'on sépare clairement les sujets : d'un côté, l'interface utilisateur représentée par Edit1 ; de l'autre, la logique métier, représentée par PrototypeBindSource1. Ce qui fera la liaison entre les deux est le contrôle de liaison de type TLinkControlToField.
III. Déclaration et instanciation de l'objet▲
Pour les besoins de l'exemple, imaginons un objet simple de type TMyObject déclaré et instancié comme suit :
type
TMyObject = class
(TObject)
private
FField1: string
;
public
constructor
Create(aField: string
);
property
Field1: string
read
FField1;
end
;
[...]
constructor
TMyObject.Create(aField: string
);
begin
FField1 := aField;
end
;
IV. Passage du modèle au réel▲
Si nous avons utilisé jusqu'ici utilisé un TPrototypeBindSource, c'est parce que nous ne pouvions pas réaliser cette modélisation avec un TObject.
Il s'agit maintenant de remplacer le TPrototypeBindSource par notre TObject, et cette action interviendra au moment de l'initialisation du TPrototypeBindSource, lors de l'événement OnCreateAdapter :
procedure
TForm4.PrototypeBindSource1CreateAdapter(Sender: TObject;
var
ABindSourceAdapter: TBindSourceAdapter);
begin
FMyObject := TMyObject.Create('value for Field1'
);
ABindSourceAdapter := TObjectBindSourceAdapter<TMyObject>.Create(self
, FMyObject, false
);
end
;
Comme l'événement OnCreateAdapter survient avant l'événement OnCreate de Form1, nous serons obligés de créer l'objet à ce moment-là.
C'est prêt. Exécutez et voyez le résultat :
V. Aller plus loin avec une liaison TStringGrid ↔ TObjectList<TMyObject> ▲
V-A. Conception via Visual LiveBindings ▲
La liste des champs de TMyObject peut ne pas être connue à l'avance. Dans ce cas, nous lierons les deux composants StringGrid1 ↔ PrototypeBindSource2 par tous leurs champs (marqués par une étoile dans le concepteur LiveBindings).
Les composants et leurs propriétés utilisés ici seront :
- StringGrid (TStringGrid) / propriétés : aucune propriété à spécifier en particulier ;
- PrototypeBindSource2 (TPrototypeBindSource) / propriétés : aucune propriété à spécifier en particulier ;
- BindingsList1 (TBindingsList) : ce composant n'est qu'un conteneur de Binding, affichable en double-cliquant dessus. Les actions réalisées dans le concepteur LiveBindings ont créé pour nous un contrôle de liaison adapté au type de liaison que nous avons choisi (lien bidirectionnel entre les cellules d'un TStringGrid et tous les champs de notre TPrototypeBindSource). Le contrôle de liaison contenu, LinkControlToField1, est du type TLinkGridToDataSource ;
- LinkGridToDataSourcePrototypeBindSource2 (TLinkGridToDataSource) / propriétés : GridControl : StringGrid1 ; DataSource : PrototypeBindSource2.
V-B. Déclaration et instanciation de TMyObject et TObjectList<TMyObject> ▲
Créons une liste d'objets contenant des objets semblables à ceux que nous avons créés plus haut, mais à l'intérieur de la méthode TObjectList<TMyObject>.AddRange :
procedure
CreateList;
begin
FMyObjectList := TObjectList<TMyObject>.Create(true
);
FMyObjectList.AddRange([
TMyObject.Create('value #1'
),
TMyObject.Create('value #2'
),
TMyObject.Create('value #3'
)
]);
end
;
V-C. Passage du modèle au réel▲
Remplaçons le TPrototypeBindSource par notre TObjectList<TMyObject>, au moment de l'initialisation du TPrototypeBindSource :
procedure
TForm4.PrototypeBindSource2CreateAdapter(Sender: TObject;
var
ABindSourceAdapter: TBindSourceAdapter);
begin
CreateList;
ABindSourceAdapter := TListBindSourceAdapter<TMyObject>.Create(self
, FMyObjectList, false
);
end
;
Vous noterez ici que le paramètre ABindSourceAdapter de type TBindSourceAdapter reçoit un autre descendant que celui reçu précédemment, car il s'agit de spécifier au framework que l'adaptateur est maintenant non pas un objet unique, mais une liste d'objets.
Exécutez et voyez le résultat :
VI. Conclusion▲
Nous avons montré ici la liaison de contrôles standards (TEdit, TStringGrid) à un descendant de TObject ou à une liste de ce descendant. Cette liaison a été réalisée avec le framework LiveBindings.
Cependant, ce framework puissant permet de réaliser beaucoup d'autres choses. Si vous voulez en savoir plus, visitez la documentation en ligne de Delphi.
Ce tutoriel est proposé par la société Barnsten.
La relecture technique et la mise en forme ont été effectuées par gvasseur58. La relecture orthographique a été effectuée par Claude Leloup.