menu
  Home  ==>  articles  ==>  bdd  ==>  visual_livebindings   

Visual LiveBindings Delphi - John COLIBRI.


1 - Les Visual LiveBindings Delphi

Avec Delphi Xe3, Delphi a introduit un concepteur visuel pour réaliser les liaisons aux données.

Rappelons que les LiveBindings permettent de lier les modifications de la valeur de propriétés de deux composants. Par exemple les modifications de Edit1.Text sont automatiquement propagées à Label1.Caption. Naturellement l'ambition de telles liaisons est de lier des sources de données (tDataset, objets métier) à des contrôles visuels, sans passer par la famille des db_xxx.

En Delphi XE2, il fallait

  • écrire ses expressions de liaison manuellement
  • écrire une notification qui propageaient les modifications d'un composant vers les composants qui lui sont liés


Ces des contraintes sont levées par un LiveBindings Designer qui permet de lier les composants par simple tirer-glisser entre composant.propriété présentés sur une surface de dessin




2 - tEdit -> tLabel LiveBindings

2.1 - tEdit -> tLabel

Notre premier exemple va propager les modifications de tEdit.Text vers tLabel.Caption
   créez une nouvelle application Xe3 Vcl "File | New | Vcl Application"
   un nouveau projet est créé
   sauvegardez-le sous "vlb_01_tedit_tlabel"
   posez un tEdit et un tLabel sur Form1
Posez aussi quelques composants qui ne sont pas destinés à être liés, comme une tCheckBox et un tPanel
   affichez le LiveBindings Designer par "View | LiveBindings Designer"
   le Designer est affiché

first_visual_livebindings

Vous noterez la parfaite synchronisation (Panel1 est sélectionné partout) entre

  • le Structure Pane (en haut au gauche)
  • l'Inspecteur d'Objet
  • le Designer (la forme)
  • le LiveBindings Designer
En cliquant sur un composant sur l'une de ces parties de l'IDE, les trois autres sélectionnent le même composant

   pour créer la liaisontEdit.Text -> tLabel.Caption, dans le LiveBindings Designer
   cliquez la propriété origine du composant origine, ici Text de Edit1
   la propriété sélectionnée devient bleue, et les propriétés de tous les autres composants qui peuvent être liés à Edit1.Text sont affichées en vert (ici Label1.Caption et Panel1.Caption)

select_source_property

   tirez-glissez la souris vers la propriété destination du champ destination, ici Label1.Caption

   un lien tLinkControl to Property est créé

link_control_to_property

   compilez et exécutez. Tapez "aaa" dans Edit1, puis Tab pour changer la focalisation
   le texte de Label1 devient "aaa"


Notez que
  • si Edit1 et Label1 étaient trop proches, le lien bleu est peut être dessiné en passant par dessus Edit1. Vous pouvez demander une réorganisation de l'affichage du Livebindings Designer en cliquant l'icône "rearrange" sur le bord gauche du Livebindings Designer.

  • Delphi a automatiquement ajouté un tBindingsList à Form1
  • le lien créé est un tLinkControlToProperty, dont les propriétés sont affichées sur la figure ci-dessus

    Delphi a automatiquement ajouté un tBindingsList à Form1

  • nous aurions aussi pu afficher le Livebindings Designer en créant une liaison sur un composant de la Forme : "composant sur la Forme | clic droit | bind visually". Dans ce cas, si le Livebindings Designer n'est pas affiché, il le sera


Pour le lien nouvellement créé
  • nous pouvons le sélectionner
    • par click dans le Livebindings Designer, naturellement
    • en le sélectionnant dans le Structure Pane, ou le Sélecteur de l'Inspecteur d'Objet
    • en affichant les lien du BindingsList1. Par double clic sur BindingsList1, nous obtenons :

      binsings_list_dialog

      et nous pouvons voir le détail de cette liaison en cliquant sur LinkControlToProperty1 :

      bindings_editor

  • nous pouvons supprimer ce lien par
    • Livebindings Designer, sélectionner le lien, "clic droit | remove link"
    • l'éditeur de BindingsList, sélection du lien, "click droit | Delete"
  • nous pouvons aussi rediriger la destination du lien en sélectionnant dans le Livebindings Designer la pointe de la flèche et en la tirant-glissant vers un autre composant (par exemple Panel1.Caption). Idem pour l'origine


Les composants (de LiveBindings) utilisés sont présentés dans les diagramme de classe UML en fin d'article



2.2 - Source Control Component

Depuis la création des LiveBindings, j'ai certaines difficultés à désigner les composants / propriétés impliqués dans la liaison.

Dans le cas le plus simple, nous avons A.B -> C.D. Mais la liaison peut être aussi bi-directionnelle, ou vers plusieurs composants.

Et les composants A et C peuvent être des simple descendants de tObject, des composants (tObject dans le .DFM) ou des contrôles visuels (tEdit etc).

"Source" de son côté est employé pour les DataSource.

Delphi (dans la documentation, les noms des composants, les wizards comme d'Editeur de liaison, l'Inspecteur d'objet) utilise une combinaison de ces termes.

Il en résulte que l'objet de départ n'est pas toujours "source" et l'objet destination "control"



Aussi essayerons nous de nous limiter au vocabulaire suivant

  • "origine" et l'objet dont les modifications seront propagées
  • "destination" l'objet qui sera mis à jour par les modifications de l'origine


Mentionnons aussi que, pour le lien tLinkControlToProperty
  • il apparaît dans les propriétés du composant origine (Edit1.LiveBindings.LinkControlToProperty), et peut être créé par "Designer | Edit1 | LiveBindings"
En revanche, pour Xe2, pour les liens tBindExpression, les liens étaient créés à partir de la destination



2.3 - Liens Multiples

Voici à présent une liaison tEdit -> Label1, Label2, Label3
   créez une nouvelle application Xe3 Vcl "File | New | Vcl Application", sauvegardez-le sous "vlb_02_tedit_many_tlabel"
   posez un tEdit et 3 tLabels sur Form1
   affichez le LiveBindings Designer par "View | LiveBindings Designer"
   dans Livebindings Designer, créez des liens entre Edit1.Text et Label1.Caption, Label2.Caption, Label3.Caption
   voici, à une réorganisation de l'affichage près, ce à quoi ressemble l'application:

several_livebindings_targets

   compilez, exécutez et modifiez Edit1.Text
   les tLabels ne sont pas modifiés


En effet, la propagation des valeurs de l'objet origine est réalisée par le changement de focalisation. Comme notre Forme ne comporte qu'un tEdit, ce changement n'est pas possible. Donc
   posez un autre composant focalisable sur Form1 (un tButton, tEdit, tCheckBox etc)
   compilez, exécutez, tapez "aaa" dans Edit1 et Tab
   les 3 labels affichent la valeur de Edit1

several_livebindings_targets_result



2.4 - tEdit -> tEdit

Pouvons nous lier un tEdit à un autre tEdit ?

Pour l'essayer :
   créez une nouvelle application Xe3 Vcl, sauvegardez-le sous "vlb_03_tedit_tedit"
   posez 2 tEdit
   affichez le Livebindings Designer par "View | LiveBindings Designer"
   lorsque dans le Livebindings Designer nous sélectionnons Edit1.Text, Edit2.Text n'apparaît pas en vert

direct_tedit_tedit_visual_livebinding_not_possible



Donc, la liaison tEdit -> tEdit n'est pas possible par les Visual Livebindings.

Il faut, pour ce type de liaison, passer par un composant intermédiaire tPrototypeBindSource.

Ce composant est destiné à permettre le prototypage de données. C'est un objet auquel nous pouvons ajouter des champs de divers types (ftInteger, ftString etc), et nous pourrons relier des composants origine et destination aux propriétés ainsi créées. Les liaisons sont bi-directionnelles

Dans le cas présent de la liaison tEdit -> tEdit, il suffit de relier les deux tEdit à une même propriété du tPrototypeBindSource, et, comme les liaisons sont bi-directionnelles, les modification d'un tEdit sera propagée à l'autre.



Commençons par lier le premier tEdit :
   posez un tPrototypeBindSource
   ajoutez un nouveau champ à cette source par "Form1 | PrototypeBindSource1 | double clic"
   l'éditeur des champs de PrototypeBindSource1 est affiché (en bas au centre)

prototypebindsoure_editor

   cliquez l'icône jaune "Add New (Ins)" en haut à gauche
   la liste des champs que nous pouvons ajouter est présentée

prototypebindsoure_add_field

   sélectionnez, par exemple, la ligne "colorsNames ftString"
   le champ est ajouté dans le Livebindings Designer

prototypebindsoure_add_colorsnames

   dans le Livebindings Designer, liez PrototypeBindSource1.ColorsName1 vers Edit1.Text
   le lien est affiché. De plus, comme le champ utilise un générateur de String (le nom des couleurs, une couleur est affichée dans Edit1 : "DarkGreen"

prototypebindsoure_tedit_binding

   liez de la même façon dans le Livebindings Designer PrototypeBindSource1.ColorsName1 vers Edit2.Text (ou réciproquement, puisque les liens sont bi-directionnels)

   voici le résultat :

tedit_prototypebindsource_tedit_binding

   ajoutez un tButton pour pouvoir changer la focalisation après modification d'un tEdit

   compilez, exécutez, tapez "aaa" dans Edit1.Text et <Tab>
   Edit2.Text devient bien "aaa"
   de la même façon, un changement de Edit2.Text serait propagée vers Edit1.Text


Comme nous avons utilisé un générateur de ftString, nous pouvons parcourir les valeurs générées en ajoutant un tBindNavigator qui nous permet de visiter les différentes valeurs:
   sur la Forme, "PrototypeBindSource1 | Clic Droit | Add Navigator"
   le navigateur est placé sur la Form1, et déjà lié à son PrototypeBindSource1

tnavigatorprototypebindsource

   compilez, exécutez, et appuyez sur les touches du navigateur pour faire défiler les valeurs dans les deux tEdits


Pour faciliter la présentation, nous avons utilisé le tPrototypeBindSource comme un générateur de valeur. Nous pouvons l'employer comme une simple tableau de champs (une sorte de tDataSet non lié à une base Sql, ou un Array of avec des champs de type quelconque. Il suffit pour cela de créer un champ qui ne soit pas associé à un générateur de valeurs aléatoires. Le générateur, spécifié dans la première colonne du dialogue de création des champs sera donc "none"
   créez une nouvelle application Xe3 Vcl, sauvegardez-le sous "vlb_04_tedit_tedit_prototype"
   posez 2 tEdit
   posez un tPrototypeBindSource
   ajoutez un nouveau champ à cette source par "Form1 | PrototypeBindSource1 | double clic"
   l'éditeur des champs de PrototypeBindSource1 est affiché
   cliquez l'icône jaune "Add New (Ins)" en haut à gauche
   la liste des champs que nous pouvons ajouter est présentée
   sélectionnez "(none) Integer"
   le champ est ajouté dans PrototypeBindSource1

   liez Edit1.Text à PrototypeBindSource1.Field1, et PrototypeBindSource1.Field1 à Edit2.Text
   sur la Forme, "PrototypeBindSource1 | Clic Droit | Add Navigator"

   compilez, exécutez
   la valeur initiale entière 0 apparaît dans les 2 edits

   tapez 44 dans Edit1 et <Tab> : la valeur est bien propagée, et dans le sens inverse aussi


Notez que

  • nous ne pouvons pas taper de valeur autre que des entiers dans les deux tEdit. Il y a donc à présent une validation qui est effectuée


Si vous tapez plusieurs valeurs, en utilisant le navigateur, ces valeurs réapparaîtront. Par conséquent le tPrototypeBindSource agit bien comme une sorte de conteneur. Donc nous pouvons bien utiliser un tPrototypeBindSource pour lier deux tEdits, mais cela a aussi pour conséquence de mémoriser les frappes, ce qui n'était pas notre intention ici.

Pour éviter cela, nous pouvons figer tPrototypeBindSource.RecordCount à 1, et les nouvelles frappes ne seront pas mémorisées (elles apparaîtront bien et seront synchronisées, mais pas mémorisées)



2.5 - Quels contrôles sont reliables ?

Pouvons-nous lier n'importe quelle propriété de n'importe quel objet (composant, contrôle), et dans n'importe quelle direction ?

Pour les contrôles, il est facile de les poser sur une tForm et d'essayer de les relier: les couleurs bleues et vertes du Livebindings Designer nous renseigneront vite.

Prenons un cas avec une tListBox, un tEdit et un tLabel:
   créez une nouvelle application Xe3 Vcl, sauvegardez-le sous "vlb_05_tlistbox"
   posez une tListBox, un tEdit et un tLabel
   affichez le Livebindings Designer
   cliquez ListBox1.SelectedValue
   voici la réponse :

tlistbox_edit_label_livebindings



Donc Edit1.Text ne peut pas être mis à jour avec une valeur sélectionnée dans ListBox1, mais Label1.Caption le peut.



Par défaut, le Livebindings Designer présente pour chaque composant, les champs que LUI suppose utile pour les LiveBindings (par exemple, pour un tEdit, la propriété Text). Nous pouvons cependant demander au Livebindings Designer de présenter d'autres champs pour la liaison.

Pour cela, nous utilisons l'ellipse "..." située en bas à droite de chaque composant.

Dans notre cas, essayons par exemple avec Edit1.Color:
   dans le Livebindings Designer, sélectionnez Edit1, cliquez l'ellipse "..." en bas à droite
   la liste de toutes les autres propriétés "bindable" sont présentées :

livebindings_designer_add_bindable_member

   cochez Color, "Ok"
   Color apparaît à présent dans la liste des champs de Edit1 qui peuvent être utilisés pour les liaisons

livebindings_designer_add_add_bindable

   cliquez ListBox1.SelectedValue
   à présent, Edit1.Color est affiché en vert, et peut être utilisé pour la liaison

livebindings_designer_add_bindable_color

Notez que l'ellipse de Edit1 est aussi en vert, indiquant que d'autres champs seraient aussi éligibles.

   liez alors ListBox1.SelectedValue -> Edit1.Color

   pour vérifier le résultat, remplissez ListBox1.Items avec quelques valeurs :
  aaa
  clRed
  255

   voici le résultat

tlistbox_tedit_databinding

   compilez, exécutez
   cliquez "aaa"
   erreur d'exécution, "aaa" ne pouvant être une couleur ("aaa is not an integer value"
   cliquez "clRed" : idem
   cliquez "255" : la boîte d'édition devient rouge
tprototypebindsource_example


En conclusion

  • je n'ai pas eu le temps d'investiguer selon quels critères Delphi décide que
    • tel composant peut être lié à tel autre
    • quelles propriétés sont affichées ou non
    • la liaison est bi-directionnelle ou unidirectionnelle
    • pour une liaison uni-directionnelle, la direction est-elle imposée (si oui par quel paramètre interne du composant)
    Je ne sais pas plus pourquoi Edit1.Text ne peut pas être simplement lié à la ligne sélectionnée d'une tListBox
  • pour les composants que nous mettons sur la palette, nous pouvons bien sûr définir cette liste. Mais un tel exercice est hors du cadre de cet article
  • a postériori, il est assez logique que "aaa" ne puisse pas se traduire par une couleur d'un tEdit. Le fait que clRed ne soit pas interprété est "normal", mais aurait pu être éventuellement converti par l'analyseur d'expression. In fine, seule la valeur "255" est enfin accepté.
    Ceci souligne bien que la mécanique des LiveBindings repose sur un analyseur d'expressions de type String
  • j'ai aussi d'utiliser un tBindExpression, tel qu'on les utilisait en Delphi Xe2 (BindingsList1 | Clic droit | New LiveBinding Component etc), mais cela a provoqué des erreurs d'exécutions


2.6 - tPrototypeBindSource

Nous avons présenté le tPrototypeBindSource lors de nos essais pour connecter deux tEdit. Mais c'était un cas particulier.

Ce composant a été créé pour permettre le prototypage. Comme déjà Il correspond à une sorte de tableau en mémoire ayant des champs de différents types: ftInteger, ftBoolean, ftString etc. (soulignons au passage le ft de ftString).

Deux cas possible:

  • nous souhaitons que des valeurs aléatoires soient générées pour que nous puissions afficher des valeurs "réalistes" (des dates, des chaînes, des nombres)
  • nous définissons simplement le type du champ, sans demander de valeur pré-générée
Lors du fonctionnement, ce tPrototypeBindSource agira comme une source de données que nous pourrons lier à des contrôles visuels, par exemple, tels que
  • un tEdit pour un champ String
  • une tCheckBox pour une valeur Boolean
  • un tMemo pour une tStrings
  • une tStringGrid pour toutes les colonnes
Ce tPrototypeBindSource peut aussi être lié à un tBindNavigator pour nous déplacer dans le tableau

Finalement, si notre application le permet (par un tBindNavigator, par exemple), nous pouvons ajouter / supprimer des lignes de données au tPrototypeBindSource.



Voici un exemple simple
   créez une nouvelle application Xe3 Vcl, sauvegardez-le sous "vlb_07_tprototypebindsource", affichez le Livebindings Designer
   posez un tPrototypeBindSource, un tEdit, tMemo, tCheckBox, tDateTimePicker (basculez ShowCheckBox à True), un tPanel, un tSpinEdit et un tStringGrid
   posez un tPrototypeBindSource
   créez un champ String, tStrings, Boolean, Date, Color et Integer
   en utilisant le Livebindings Designer, associez ces champs aux contrôles correspondants (pour Panel1, il faut ajouter le champ "bindable" Color, et pour DateTimePicker le champ Date)
   sur la Forme, "PrototypeBindSource1 | Clic Droit | Add Navigator"
   voici la situation

tprototypebindsource_livebindings_designer

   compilez, exécutez
   voici le résultat

tprototypebindsource_example



Notez que

  • nous revisiterons le tPrototypeBindSource dans le cadre des tDataSet, ou il permettra une visualisation de données générées à la conception et pourra être remplacé à l'exécution par les données réelles
  • l'affichage dans le Livebindings Designer peut très vite devenir confus. L'icône "Rearrange" peut éventuellement améliorer les choses, mais n'est pas parfait. La réorganisation par l'algorithme de Sujiyama de EssModel était, je pense bien meilleure
  • mentionnons aussi que lors de cet essai, j'ai bloqué Xe3. Mettez vous donc en mode "Edit AutoSave" et sauvegardes fréquemment. Cave emptor !



3 - tDataset et tBindSourceDb

Nous pouvons aussi utiliser les Visual LiveBindings pour relier les tDataSets aux contrôles visuels, en utilisant un tBindSourceDb.

Voici un exemple :
   créez une nouvelle application Xe3 Vcl, sauvegardez-le sous "vlb_08_tbindsourcedb", affichez le Livebindings Designer
   posez un tDataSet sur votre forme et ouvrez le. Dans notre cas, nous avons utilisé un tClientDataset, donc
   copiez un exemple de fichier .CDS (ou .XML) d'un tClientDataset, par exemple (tout à fait par exemple), FISHFACTS.CDS (dans "Program Files", et nous avons une copie dans le .ZIP). Copiez le dans un répertroire proche du projet
   posez un tClientDataSet
   initialisez ClientDataSet1.FileName au nom de fichier de FISHFACTS.CDS. ATTENTION, dans notre exemple l'adresse est absolue. Il faudra donc la modifier si vous utilisez le .ZIP
   basculez Active à True
   ClientDataSet1 est affiché avec tous ses champs dans le Livebindings Designer
   voici la situation:

tclientdataset

   pour ajouter des contrôles qui afficheront les champs du ClientDataSet1, dans le Livebindings Designer
   sélectionnez le champ que vous souhaitez afficher, par exemple "Category"
   "clic droit | Link to New control"
   une liste de contrôles que nous pouvons utiliser pour afficher ce champ sont affichés :

livebindings_designer_bind_to_new_control

   sélectionnez tEdit, par exemple

   le Livebindings Designer a
  • ajouté un tBindingsList à Form1
  • ajouté un tBindSourceDb à Form1
  • dans le Livebindings Designer, ClientDataSet a été niché dans le BindSourceDb1
  • un tEdit a été ajouté à Form1 et a lié ClientDataset1.Category à Edit1.Text
tbindsourcedb_tclientdataset_add_binging

   procédez de même pour quelques autres champs, et sur le champ "*", choisissez une tStringGrid

   depuis la Tool Palette, ajoutez un tBindNavigator, puis dans le Livebindings Designer, liez ClientDataSet1."*" à ce navigateur

   voici le résultat de ce bel effort :

tbindsourcedb_tclientdataset

et voici le résultat de l'exécution

tbindsourcedb_tclientdataset_display



Notez que :

  • nous avons profité de cet exemple pour montrer comment créer un contrôle et le lier directement depuis le Livebindings Designer
  • nous aurions très bien pu procéder en
    • posant un tBindSourceDb depuis la Tool Palette
    • initialisant tBindSourceDb.DataSet à ClientDataSet1
    • posant les autres contrôles et les liant via le tBindSourceDb
  • le tBindNavigator aurait pu être ajouté par "Form1 | clic droit | Add Navigator"


Nous pouvons aussi utiliser des tBindExpression que nous utilisons déjà en Delphi Xe2.

Lions, par exemple ClientDataSet.Length_in à un tEdit.Text :
   créez les champs persistents de ClientDataSet1 : "Form1 | ClientDataSet& | clic clic | Add All Fields"
   posez un tEdit sur Form1
   "Form1 | BindingsList1 | clic droit | New LiveBinding Component | tBindExpression"
   une nouvelle expression est créée
   dans l'Inspecteur d'Objet, ajoutez ses paramètres origine et destination :
  • SourceComponent : ClientDataSet1Length_In
  • SourceExpression : Value
  • ControlComponent : Edit1
  • ControlExpression : Text

   voici le résultat (sur cette image, nous avons aussi réaffiché la liste des bindings : "LiveBindings1 | clic clic")

add_binging

   pour provoquer la modification de Edit1, créez ClientDataSet1.OnScroll, et tapez

Procedure TForm1.ClientDataSet1AfterScroll(DataSetTDataSet);
  Begin
    BindingsList1.Notify(ClientDataSet1Length_In'');
  End;

   compilez et exécutez


Notez que

  • ClientDataSet1.OnScroll n'est peut être pas le meilleur événement pour provoquer le recalcul (nous avions essayé BindSourceDB1SubDataSourceDataChange mais sans succès. Faute de grives ...)



4 - le LiveBindings Designer

Le Livebindings Designer possède tout un tas de fonctionnalités, largement présentées sur le Web, et que nous ne ferons que lister
  • icônes sur la gauche (ajuster, zoomer, réarranger ...)
  • possibilités de travailler en "couches" (icône "Layers" et le "+" en haut à droite), avec possibilités de créer des couches, leur associer certains composants etc
  • menus contextuels pour
    • effectuer / ajouter / supprimer des liaisons, des composants
    • cacher temporairement des composants non utiles pour les liaison
    • sauvegarder en tant que bitmap


Mentionnons aussi que les styles sont sauvegardés dans un fichier .VLB. Ce fichier est optionnel, car nous pouvons générer l'affichage dans le LiveBindings Designer par "view | LiveBindings Designer". Le fichier conserve néanmoins vos modifications personnelles (placement manuel, couches, composants cachés etc).




5 - Nos propres Classes

5.1 - c_person tStringGrid tAdapterBindSource

5.2 - Principes généraux

A présent nous allons utiliser les LiveBindings pour afficher les données de notre Classe c_person

Pour cela il faut que notre objet utilise un adaptateur qui convertit nos champs en propriétés liables à des contrôles visuels. C'est la fonction du tAdapterBindSource.



Mais pendant la conception, notre objet n'existe pas encore (sauf si nous avions crée un composant sur la Palette, ce qui n'est pas le cas et n'est pas nécessaire).

Il faut donc que quelqu'un fasse office de conteneur de champs "FirstName", "Age" etc.

Pour cela, nous utilisons un tDataGeneratorAdapter en créant des champs ayant le nom et le type des champs de notre objet.



En gros

  • c_person + tDataGeneratorAdapter jouent le rôle d'un tDataSet
  • tAdapterBindSource joue le rôle d'un tDataSource
ce qui peut être représenté ainsi :

04_tdatageneratoradapter



Pour que notre véritable objet c_person se substitue à l'exécution au générateur utilisé simplement pendant la conception pour avoir des champs à lier, il faut impérativement créer l'événement tAdapterBindSource.OnCreateAdapter, en fournissant un objet réel à lier. Cet événement OnCreateAdapter sera automatiquement exécuté au lancement de l'application, et effectuera la substitution à ce moment.



Ici aussi les noms deviennent un peu difficile à mémoriser : tous ce "bind", "adapter", "source" "data". Seule petite main secourable mnémonique:

  • "data" du côté des données tDataset, tDataGeneratorAdapter
  • "source" du côté du conduit : tDataSource et tAdapterBindSource


5.3 - L'objet à gérer

Notre Classe est une c_person tout à fait classique :
   voici le type:

Type (*$m+*)                                                                                                                                                        //
     c_person// one "person"
         Class
           // -- m_name:
           Private
             FFirstNameString;
             FAgeinteger;
             FMarriedboolean;
             Procedure SetMarried(Const Valueboolean);
             Procedure SetFirstName(Const ValueString);
             Procedure SetAge(Const Valueinteger);
           Public
             Constructor create_person(p_nameString);
           Published
             Property FirstNameString read FFirstName write SetFirstName;
             Property Ageinteger read FAge write SetAge;
             Property Marriedboolean read FMarried write SetMarried;
         End// c_person
     (*$m-*)

     t_cp_person_listtObjectList<c_person>;

avec deux fonctions de création:

Function f_c_create_and_fill_person(p_firstnameStringp_ageInteger;                                                    //
    p_marriedBoolean): c_person;
  Begin
    Result:= c_person.create_person(p_firstname);
    Result.Age:= p_age;
    Result.Married:= p_married;
  End// f_c_create_and_fill_person

Function f_c_create_and_fill_person_listt_cp_person_list;

  Procedure add_person(p_firstnameStringp_ageIntegerp_marriedBoolean);
    Begin
      Result.Add(f_c_create_and_fill_person(p_firstnamep_agep_married));
    End// add_person

  Begin // f_c_create_and_fill_person_list
    Result:= tObjectList<c_person>.Create;

    add_person('SMITH', 11, False);
    add_person('MILLER', 22, True);
    add_person('BROWN', 33, True);
  End// f_c_create_and_fill_person_list




5.4 - tDataGeneratorAdapter

Pour que le Livebindings Designer puisse opérer sur des champs, nous utilisons, pour la phase conception, un tDataGeneratorAdapter, en créant les champs qui joueront le rôle des champs de notre objet
   posez sur Form1 un tDataGeneratorAdapter
   ajoutez, par exemple, les champs correspondant à c_person.Firstname et c_person.Age, (nous pourrions ajouter Married) :
   "Form1 | DataGeneratorAdapter | double clic",
  • sélectionnez "non / ftString"
  • PUIS donnez le nom du champ pour qu'il corresponde à celui de c_person, à savoir "FirstName"
   voici la situation :

tdatageneratoradapter_add_field

   ajoutez de même un champ "none / ftInteger" et fieldname "Age"
   et le résultat :

tdatageneratoradapter_filled



5.5 - tAdapterBindSource

Puis la liaison entre les données et la partie visuelle :
   ajoutez un tAdapterBindSource
   dans l'Inspecteur d'Objet, initialisez AdapterBindSource1.Adapter à DataGeneratorAdapter1
   dans le Livebindings Designer, DataGeneratorAdapter1 se niche dans AdapterBindSource1

   dans le Livebindings Designer, sélectionnez AdapterBindSource1.FirstName, "clic droit | link to new control | tEdit" et de même pour age

   l'ensemble est à présent le suivant :

tdatageneratoradapter_c_person



Pour que notre véritable objet c_person se substitue à l'exécution au générateur, il faut impérativement créer l'événement tAdapterBindSource.OnCreateAdapter, en fournissant un objet à lier.

Voici comment procéder:
   créez une variable globale (ou un champ de Form1) de type c_person
   sélectionnez AdapterBindSource1
   dans l'Inspecteur d'Objet, créez son événement OnCreateAdapter
   tapez le code suivant:

Var g_c_one_personc_personNil;                                                                                                                 //

Procedure TForm1.AdapterBindSource1CreateAdapter(SenderTObject;
    Var ABindSourceAdapterTBindSourceAdapter);
  Begin
    g_c_one_person:= f_c_create_and_fill_person('LEWIS', 33, True);
    aBindSourceAdapter:= tObjectBindSourceAdapter<c_person>.Create(
        Nilg_c_one_person);
  End// AdapterBindSource1CreateAdapter

  • le premier paramètre est le propriétaire de tObjectBindSource
  • le second l'objet qui est "adapté"
  • True : libérera l'objet

   compilez, exécutez
   voici le résultat

tdatageneratoradapter_c_person_exec



Notez que

  • les champs ce c_person sont éditables, gardent les valeurs tapées etc


Nous pouvons procéder de même avec une liste d'objet :
   notre tObjectList<c_person> a déjà été présenté, ainsi que la fonction qui le créé et génère quelques valeurs
   posez sur Form1 un deuxième tDataGeneratorAdapter1 et créez ses champs, en utilisant, pour changer, des valeurs aléatoire générées lors de la conception (surtout utilisées pour la mise en page)
  • "loremipsum ftString" et FieldName "Firstname"
  • "integers ftInteger" et FieldName "Age"
   ajoutez un deuxième tAdapterBindSource
   dans l'Inspecteur d'Objet, initialisez AdapterBindSource2.Adapter à DataGeneratorAdapter2
   dans le Livebindings Designer, DataGeneratorAdapter2 se niche dans AdapterBindSource2
   dans le Livebindings Designer, sélectionnez AdapterBindSource2."*", "clic droit | link to new control | StringGrid"
   dans le Livebindings Designer, DataGeneratorAdapter1 se niche dans AdapterBindSource1, et Form1 affiche des valeurs "réalistes" (encore que des zigs appelé LoremIpsum j'en ai rarement rencontré dans la rue)

   pour que la substitution se fasse au lancement de l'EXE, créez l'événement tAdapterBindSource2.OnCreateAdapter et tapez le code de liaison :

Var g_c_person_listtObjectList<c_person>= Nil;

Procedure TForm1.AdapterBindSource2CreateAdapter(SenderTObject;
    Var ABindSourceAdapterTBindSourceAdapter);
  Begin
    g_c_person_list:= f_c_create_and_fill_person_list;
    aBindSourceAdapter:= tListBindSourceAdapter<c_person>.Create(
        Nilg_c_person_list);
  End// AdapterBindSource2CreateAdapter

   vous pouvez aussi ajouter un navigateur par "tAdapterBindSource2 | clic droit | add navigator"

   compilez, exécutez
   voici le résultat

tlistbindsourceadapter



Notez que
  • pour tDataGeneratorAdapter2 nous aurions pu copier / coller la première version, et simplement changer les champs Generator des Fields (ce que j'ai fait lorsque j'ai perdu tous les fichiers du projet suite à une fausse manip. Aucun doute, au bout de deux ou trois passes du même exercice, tout commence à ronronner)


5.6 - tAdapterBindSource

Ce composant se comporte comme un tDataSet. Par exemple
  • il a
    • des propriétés State
    • des fonctions Bof, Eof
    • des méthodes Edit, Insert, Append, Post et Cancel
    • des événements Before et After pour Insert, Open, Post, Scroll, etc
  • quand nous modifions un tEdit, le tNavigator change d'état
  • quand nous modifions la valeur d'un champ dans un tEdit, et que nous donnons la focalisation à un autre champ, les données ne sont pas encore envoyées à l'objet, mais sont tamponnées dans l'Adapter. Le Post implicite ou explicite font le transfert vers l'objet (comme pour le tampon d'un tDataSet)


Nous avons aussi des possibilités de formatter les champs (non présenté ici)



Nous aurions aussi pu utiliser

  • un générateur avec un tDataSet
  • un tPrototypeBindSource qui est une combinaison de tAdapterBindSource et d'un tDataGeneratorAdapter



6 - Visual LiveBindings et Actions

Nous pouvons aussi utiliser des tActions. En effet de nouvelles actions standard ont été créées pour permettre, par exemple, les déplacements dans un tableau de données lié à l'aide de LiveBindings.



Voici un exemple où un tButton provoque le "next" dans un tPrototypeBindSource:
   créez une nouvelle application Xe3 Vcl, sauvegardez-le sous "vlb_06_livebindings_and_actions"
   ajoutez un tPrototypeBindSource, créez quelques champs (un "LoremIpsum" et un tDateTime
   affichez le Livebindings Designer, et liez "*" à un tStringGrid, le champ ftString à un tEdit et le champ ftDateTime à un tLabel
   nous en sommes arrivés là :

databindings_and_actions_start

   posez une tActionList sur Form1
   posez un tButton sur Form1
   dans l'Inspecteur d'Objet, selectionnez "Actions | dropdown | New Standard Action | LiveBindings | tBindNavigatorNext"

databindings_and_actions

   sélectionnez cette action dans l'Inspecteur d'Objet (dans le Structure pane, le sélecteur de l'inspecteur d'objet, ou en affichant le liste des actions par double clic sur ActionList1 et sélection de l'action)
   initialisez la propriété Datasource de LiveBindingsBindNavigateNext1 à PrototypeBindSource1

   compilez, exécutez
   voici le résultat

databindings_and_actions_result



Notez que nous pourrions naturellement utiliser les actions avez un tBindSourceDb




7 - Diagramme de Classe Uml Visual LiveBindings

Voici deux diagrammes de classe UML qui font la synthèse des composants utilisés par notre présentation des Visual LiveBindings.

Ces diagrammes comportent pas mal de classes intermédiaires, mais donnent une vue d'ensemble plus globale que chaque exemple ou que la Tool Palette. Notez aussi que je n'ai pas repris la mécanique sous-jacente des iScope, iLocation, iCompileBinding qui ont été présentés dans un autre article, et qui n'apporteraient rien à ce panorama des composants utilisés pour les Visual LiveBindings.

Nous avons présenté en gras les composants utilisés ici. Tous les liens n'ont pas été présentés pour éviter de surcharger les figures



Voici tout d'abord les générateurs de tout poil :

01_tadapterbindsource



Puis les composant utilisés par "link to component" :

uml_class_diagram



Et la partie permettant les liaisons avec les tDataSet (avec le BindScopeDb utilisé par Delphi Xe2)

uml_class_diagram




8 - Quelques remarques

8.1 - Utilisation de Delphi Xe3

Quelques petits sousis
  • "Out of Memory" (avec une version Xe3 d'origine, Septembre 2012, sans mise à jour). C'est sans doûte lié à mon installation, car l'utilisation de Xe3 bloque aussi la fermeture de Windows, obligeant à couper l'interrupteur.
  • supprimer un tAdapterBindSource (ou peut-être était-ce le générateur) de Form1 (par "delete") provoque un plantage de l'IDE. Gestionnaire de tâches, supprimer Xe3 etc. Cela m'a peut être aussi fusillé les fichiers sauvegardés. J'ai été rempli de joie et d'allégresse ...

    Moralité: j'ai alors plus fréquemment rechargé Delphi Xe3 pour éviter que des reliquats d'erreurs ne polluent les nouveaux projets.



8.2 - Compatibilité arrière Xe2

Nous ne pouvons pas directment appliquer notre expérience des LiveBindings Xe2 aux LiveBindings Xe3
  • lorsque nous cliquons sur txxx.Livebindings dans l'inspecteur d'objet, c'est "bind visually" qui apparaît
  • les composants utilisés jadis ont été largement supplatés par les nouveaux composants (tLinkControlToField etc)
  • la métaphore "toujours créer le LiveBinding à partir du composant destination" ne s'applique plus dans ce cas. Le composant LiveBinding est à présent niché dans le composant origine


Nous ne pouvons donc plus réutiliser les techniques de mise en oeuvre présentées dans notre précédent tutorial.

En revanche, nous pouvons toujours créer les liens manuellement, comme nous l'avons présenté plus haut

  • poser un tBindingsList
  • "clic droit | New LiveBinding Component | sélection du composant de liaison" (par exemple un tBindExpression)
  • puis sélection du composant de liaison et initialisation de ses propriétés dans l'Inspecteur d'Objet


A mon avis, les anciens composants de liaison "haut niveau" vont peu à peu disparaître de la scène, étant avantageusement remplacés par les nouveaux composants accessibles par les Visual LiveBindings.



8.3 - LiveBindings Xe2 et Xe3

Les Visual LiveBindings sont un grand progrès par rapport à l'écriture des expressions de liaisons telles qu'elles se pratiquaient sous Delphi Xe2.

L'obsolescence possible des anciens composants de liaison incite à migrer rapidement de Xe2 vers Xe3. Cela pose le problème de la stabilité de la mécanique, puisqu'au bout d'un an, les applications devront être aménagées. Un problème similaire se pose d'ailleurs aussi pour FmxFm2, sans parler de iOsmobile studio.

Nous avions essayé de tempérer l'enthousiastme immense suscité par Delphi Xe2, en mentionnant timmidement que FireMonkey et LiveBindings semblaient un peut jeunes.

Souhaitons que la nouvelle version Visual Livebindings soit une base stable pour les évolutions futures.



8.4 - LiveBindings, So What ?

Tout le monde a compris que les LiveBindings évitaient l'écriture de la double hiérarchie t_xxx / t_db_xxx pour FireMonkey, en nichant la sensibilité aux données beaucoup plus haut dans la hiérarchie, comme le fait .Net.

Mais quel intérêt pour les utilisateurs de la VCL ?

Certes nous pouvons lier des composants autres que les tDataSet. Mais avons nous souvent à lier un tEdit à un tLabel . Certes non.

En réalité la véritable mine se trouve du côté de la séparation GUI / Gestion des données (tDataSet directs ou via des objets métier). Nous arrivons là dans la galaxie des MV_xxx (MVC, MVP, MVG, MP, PF, MVVM et je crois qu'on en invente de nouveaux tous les lundi, mercredi et vendredi). Et qui nous offre la voie royale vers la testabilité des applications, et mieux encore, TDD: Test Driven Developpment.




9 - Télécharger le code source Delphi

Vous pouvez télécharger: Mentionnons que la taille d'environ 80 K est surtout due à la taile du .RES



Comme d'habitude:

  • nous vous remercions de nous signaler toute erreur, inexactitude ou problème de téléchargement en envoyant un e-mail à jcolibri@jcolibri.com. Les corrections qui en résulteront pourront aider les prochains lecteurs
  • tous vos commentaires, remarques, questions, critiques, suggestion d'article, ou mentions d'autres sources sur le même sujet seront de même les bienvenus à jcolibri@jcolibri.com.
  • plus simplement, vous pouvez taper (anonymement ou en fournissant votre e-mail pour une réponse) vos commentaires ci-dessus et nous les envoyer en cliquant "envoyer" :
    Nom :
    E-mail :
    Commentaires * :
     

  • et si vous avez apprécié cet article, faites connaître notre site, ajoutez un lien dans vos listes de liens ou citez-nous dans vos blogs ou réponses sur les messageries. C'est très simple: plus nous aurons de visiteurs et de références Google, plus nous écrirons d'articles.



10 - Références et Liens divers

  • Jim Tierney
    Les sources correspondantes sont parmi les démos SourceForge (cf ci-dessous)

  • Malcolm GROVES
  • sources de démos LiveBindings: télécharger par la ligne de commande suivante :

      svn co https://radstudiodemos.svn.sourceforge.net/svnroot/radstudiodemos/branches/RadStudio_XE3/LiveBindings

    Si vous souhaitez un topo sur SVN, voyez l'article SubVersion avec Windows installation et utilisation du gestionnaire de version SubVersion : création du Repository, ajout d'un répertroire au repository, création d'une la Working Copy, commandes pour ajouter, committer, effacer, installation du serveur SVNSERV.EXE et son utilisation en réseau, Svn et Delphi XE

  • nos articles précédents concernant les livebindings Xe2 :
    • LiveBindings Delphi XE2 : les LiveBindings permettent d'évaluer dynamiquement des expressions entre propriétés de composants source et cible. La définition des relations sous forme de String offre une grande flexibilité et est plus générale que les db_xxx de la VCL. Ces LiveBindings seront surtout utilisée par FireMonkey, en particulier pour les bases de données

    • Architecture LiveBindings Delphi : analyse de l'architecture des LiveBindings Delphi : comment tBindingExpression compile une expression au format String pour construire un environment qui référence des tObject et évalue cette expression pour remplir les propriétés de composants. Affichage du pseudo-code généré. Diagramme de classe UML de l'architecture des LiveBindings

    Nous avions aussi présenté une conférence CodeWay:
    • Conférence LiveBindings Delphi XE2 : pour la conférence CodeWay 5, nous présenterons le jeudi 24 Novembre de 11 heures à 12 heures les LiveBindings Delphi XE2 (iScope, tBindingExpression, les Methods standard et "custom", liaisons managées et non managées, les Converters standards et "custom", Notify, l'Editeur d'Expression) - 1 heure, 12 démos

  • pour la réorganisation de diagrammes en minimisant les croisement de liens, l'algorithme de Sujiyama est utilisé par EssModel. Cet utilitaire gratuit en source réalise le reverse engineering UML d'un .DPR Delphi. Et utilise notamment cet algorithme de mise en page pour les diagrammes de classe.



11 - L'auteur

John COLIBRI est passionné par le développement Delphi et les applications de Bases de Données. Il a écrit de nombreux livres et articles, et partage son temps entre le développement de projets (nouveaux projets, maintenance, audit, migration BDE, migration Xe_n, refactoring) pour ses clients, le conseil (composants, architecture, test) et la formation. Son site contient des articles avec code source, ainsi que le programme et le calendrier des stages de formation Delphi, base de données, programmation objet, Services Web, Tcp/Ip et UML qu'il anime personellement tous les mois, à Paris, en province ou sur site client.
Créé: jan-13. Maj: fév-13.  148 articles, 379 sources .ZIP, 2.021 figures
Contact : John COLIBRI - Tel: 01.42.83.69.36 / 06.87.88.23.91 - email:jcolibri@jcolibri.com
Copyright © J.Colibri   http://www.jcolibri.com - 2001 - 2013
Retour:  Home  Articles  Formations  Développement Delphi  Livres  Pascalissime  Liens  Download
l'Institut Pascal

John COLIBRI

+ Home
  + articles_avec_sources
    + bases_de_donnees
      + programmation_oracle
      + interbase
      + sql_server
      + firebird
      + mysql
      + xml
      – paradox_via_ado
      – mastapp
      – delphi_business_objects
      – clientdataset_xml
      – data_extractor
      – rave_report_tutorial
      – visual_livebindings
      – migration_bde
    + web_internet_sockets
    + prog_objet_composants
    + office_com_automation
    + colibri_utilities
    + uml_design_patterns
    + graphique
    + delphi
    + outils
    + firemonkey
    + vcl_rtl
    + colibri_helpers
    + colibri_skelettons
  + formations
  + developpement_delphi
  + présentations
  + pascalissime
  + livres
  + entre_nous
  – télécharger

contacts
plan_du_site
– chercher :

RSS feed  
Blog

Formation Bases de Données Interbase / Firebird Gestion de bases de données : les composants Ibx, connexion, accès aux tables, édition d'états - 3 jours
Formation Programmation Objet Delphi La programmation objet: les types, l'encapsulation, l'héritage, le polymorphisme - 3 jours