Didier Cabalé – MVP Embarcadero
LiveBindings avec TObject ou TObjectList
Dès ses début, Delphi a voulu se positionner en tant qu’outil RAD (Rapid Application Development)
La philosophie du RAD était de pouvoir dessiner l’application avec des composants et des contrôles visuels, en définissant leurs propriétés (via l’inspecteur d’objet), 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ée. Ex: lorsque je scrolle dans mon dataset, la donnée représentée par mon contrôle se met à jour automatiquement.
Dans ce cadre là, Delphi mettait à disposition du développeur des contrôles « sensibles aux donnés » (data aware): quand on voulait relier un TEdit, ou un TComboBox, ou un TStringGrid à une source de donnée, on utilisait alors, toujours pour rester dans cet esprit RAD, des TDBEdit, TDBCombobox, 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ée. Et si je voulais relier mon Txyz à autre chose qu’à une source de donnée, à un autre composant, ou à un autre objet par exemple, je ne le pourrais 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ée.
L’objet de cet article est donc de montrer comment lier un contrôle visuel (TEdit, TStringGrid) à un Objet (descendant d’un TObject)
- En conception, liaison du contrôle visuel avec un TPrototypeBindSource
L’objet du TPrototypeBindSource est de créer une source de donnée virtuelle, censée représenter le TObject que nous voulons utiliser par la suite, dont nous pourrons définir le comportement vis à vis du composant auquel il sera lié.
Depuis notre « Form Designer »:
- Ajoutons un TPrototypeBindSource. Depuis son inspecteur d’object et sa propriété FieldDefs (TGeneratorFieldDefs), ajoutons un TGeneratorFieldDef -> TGeneratorFieldDefs[0]. A ce TGeneratorFieldDefs[0] nouvellement créé, spécifions la valeur ‘FField1’ à 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é pour notre contrôle visuel, en utilisant les graphes de liaison du « LiveBindings Designer »
Depuis le [LiveBindings Designer]:
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’, affichables en double-cliquant dessus. Les actions réalisées dans le LiveBindings designer, ont créé pour nous un « contrôle de liaison », adapté au type de liaison que nous avons choisi (lien bi-directionnel 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: PrototypeBindSource; FieldName: FField1; Direction: linkBidirectional
** Note **
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 « LiveBindings designer ».
ex: pour un TEdit, le membre « observable » enregistré est sa propriété « Text ».
** Important **
Vous noterez que le Edit1 n’a aucune connaissance du composant avec lequel nous voulons le lier, PrototypeBindSource1. De la même façon, PrototypeBindSource1 n’a aucune connaissance du contrôle avec on veut le lier (Edit1)
Ceci est primordial, car celà montre que l’on sépare clairement les sujets: d’un côté l’interface utilisateur, représenté par Edit1; de l’autre la logique métier, représenté par PrototypeBindSource1.
Ce qui fera la liaison entre les deux est le contrôle de liaison, de type TLinkControlToField.
- Déclaration et instanciation de notre 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;
- 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]
** Note **
Comme cet événement survient avant l’événement [OnCreate] de Form1, nous serons obligés de créer l’objet à ce moment là.
procedure TForm4.PrototypeBindSource1CreateAdapter(Sender: TObject;
var ABindSourceAdapter: TBindSourceAdapter);
begin
FMyObject := TMyObject.Create(‘value for Field1’);
ABindSourceAdapter := TObjectBindSourceAdapter<TMyObject>.Create(self, FMyObject, false);
end;
C’est prêt. Exécutez et voyez le résultat:
- Allons plus loin, avec une liaison TStringGrid <-> TObjectList<TMyObject>
4.1. 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 champ (marqué par une étoile dans le « LiveBindings Designer »)
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’, affichables en double-cliquant dessus. Les actions réalisées dans le LiveBindings designer, ont créé pour nous un « contrôle de liaison », adapté au type de liaison que nous avons choisi (lien bi-directionnel 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
4.2. Déclaration et instanciation de nos objets, TMyObject et TObjectList<TMyObject>
Créons une liste d’objets contenant des objets semblables à ceux que nous avons créé en 2., 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;
4.3. Passage du « modèle » au « réel »:
Remplacement du 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: TBindSourceAdapter reçoit un autre descendant que celui reçu dans le 3., car il s’agit de spécifier au framework que l’adapteur est maintenant, non pas un objet unique, mais une liste d’objets.
Exécutez et voyez le résultat:
Conclusion:
Nous avons montré ici la liaison de contrôles standards (TEdit, TStringGrid) à un descendant de TObject ou a 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 ceci -> http://docwiki.embarcadero.com/RADStudio/Berlin/fr…
Vous trouverez les codes sources ici: https://www.barnsten.com/media/LiveBindings_Objects.zip