menu
  Home  ==>  articles  ==>  firemonkey  ==>  livebindings_delphi_xe2   

LiveBindings Delphi XE2 - John COLIBRI.

  • résumé : 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
  • mots clé : tBindingsList - tBindExpression - SourceComponent, SourceExpression ControlComponent et ControlExpression - tBindScope - tBindLink
  • logiciel utilisé : Windows XP personnel, Delphi XE2, Update 1
  • matériel utilisé : Pentium 2.800 Mhz, 512 Meg de mémoire, 250 Giga disque dur
  • champ d'application : Delphi XE2 / FireMonkey
  • niveau : développeur Delphi
  • plan :


1 - LiveBindings Delphi XE2

Les LiveBindings permettent d'évaluer dynamiquement des expressions. En général il s'agit de lier les propriétés de deux objets par des expressions sous forme de Strings qu'un évaluateur peut mettre à jour en fonction des valeurs à l'exécution.

L'utilisation de String pour spécifier à la fois la source des calculs et la cible à mettre à jour offre plus de flexibilité que les liaisons statiques.

Les LiveBindings vont donc au delà des composants sensibles aux données, car

  • ces liaisons permettent de relier des données autres que celles provenant des bases de données
  • les expression étant définies sous forme de string peuvent être modifiées à l'exécution
Cette flexibilité se paye néanmoins par une complexité accrue et tous les risques liés à l'évaluation dynamique. Plusieurs moyens, que nous allons présenter, permettent de vérifier à la conception la correction de nos expressions.

Mentionnons aussi que les LiveBindings sont LE moyen pour gérer en lecture ou écriture les bases de données avec FireMonkey, et donc d'accéder à ces données pour Win32, Win64, Mac OsX et iOs (iPhone, iPad, TouchPad)




2 - La première LiveBinding

Nous allons lier
  • un tSpinEdit (un tEdit lié à un tUpDown pour faciliter la saisie)
  • un tLabel qui affichera la valeur convertie en Euros (multiplié par 1.30)
Dans ce cas
  • le composant qui est à l'origine de la modification est appelé la SOURCE
  • le composant dont la valeur est modifiée est appelé le CONTROLE
Pour propager les modifications des valeurs de la Source, il faut un composant intermédiaire. Dans notre cas, nous utiliserons un tBindExpression. Sur ce composant intermédiaire, il faudra préciser 4 informations
  • le composant source et l'information source
  • le composant de contrôle et la valeur à modifier
Cette tBindExpression fera partie d'une tBindingsList qui pourra contenir plusieurs composants de liaison.



Voici comment procéder
   créez une application VCL (ou FireMonkey) par "File | New | Vcl Application"
   de l'onglet "Samples" de la Tool Palette, sélectionnez un tSpinEdit et posez sur le Forme, nommez le "source_spinedit_"
   posez un tEdit et nommez-le "control_edit_"

   créez un tListBindings en
  • sélectionnant CONTROL_EDIT_ dans l'Inspecteur d'Objet
  • puis en cliquant sur sa propriété LiveBindings
   une invite propose "New Livebinding"

create_control_livebindings

Notez que au bas de l'Inspecteur d'Objet, "New LiveBinding ..." est aussi proposé pour ouvrir l'Editeur de LiveBindings (flèche verte)

   cliquez "New LiveBinding"

   le dialogue de "New LiveBinding" est affiché

new_livebinding_dialog

Notez qu'un tBindingsList a aussi été ajouté à la Forme pour regrouper nos LiveBindings (flèche verte)

   sélectionnez tBindExpression (par défaut) et cliquez "Ok"

   BindExpressionsource_spinedit_1 a été ajouté à control_spinedit_.LiveBindings (sous propriété). Ceci est aussi visible dans le Structure TreeView:

control_bindexpression

   sélectionnez BindExpressionControl_edit_1 (soit à l'aide du Structure TreeView, soit dans control_edit_.LiveBindings de l'Inspecteur d'Objet

   l'Inspecteur d'Objet présente les propriétés de tBindExpression:

bindexpression

   remplissez les 4 propriétés nécessaires pour la liaison (flèches vertes et bleues)
  • sélectionnez SourceComponent, ouvrez la combobox et sélectionnez source_spinedit_
  • sélectionnez SourceComponent et tapez

      Value

    Value est la valeur du tSpinEdit, ce qui correspond à l'expression de départ: source_spinedit_.Value

  • pour ControlComponent, control_edit_ est déjà sélectionné
  • dans ControlExpression, tapez

      Text

    qui est la propriété qui sera modifiée (control_edit_.Text)

   voilà le résultat:

sourceexpresson_and_controlexpression

   compilez en mode débug, en cliquant sur la flèche avec le bug rouge

run_with_debut

   la valeur de l'Edit contient bien la valeur de la source, 0

livebindings_first_evaluation



Toutefois, si nous modifions la valeur de la source, la valeur du contrôle n'est pas modifiée. Il faut en effet demander manuellement la mise à jour du calcul. Cela se fait en utilisant un événement quelconque de la source pour demander que l'expression de la cible soit réévaluée. Ici il s'agit, naturellement de l'événement tSpinEdit.OnChange
   sélectionnez source_spinedit_ et créez son événement OnChange
   dans source_spinedit_Change tapez le code qui provoque le recalcul par BindingsList1  :

Procedure TForm4.source_spinedit_Change(SenderTObject);
  Begin
    BindingsList1.Notify(source_spinedit_'Value');
  End// source_spinedit_Change

   compilez et exécutez. Modifiez la valeur du spin edit
   la valeur du contrôle est bien modifiée:

tbindinglist_notify



Notez que

  • le choix de la terminologie nous semble un peu malheureuse: "contrôle" est trop connoté (un tControl Windows, ou un composant qui pourrait "contrôler" un autre composant). Nous aurions préféré "destination" ou "target". D'ailleurs certains passages de l'aide mentionnent bien "Input" et "Output" plutôt que "source" et "contrôle"

    A l'évidence cette dénomination provient des bases de données:

    • le tDataSource
    • les contrôles (au sens Windows) dbGrid, dbEdit etc

  • ATTENTION, pour créer le LiveBindings, il faut cliquer sur le composant CONTROLE.

    Spontanément nous aurions envie de cliquer sur le composant SOURCE. En effet, l'information coulera bien de la source vers le contrôle.

    Mais comme une source pourra alimenter plusieurs contrôles, et que chaque contrôle n'a qu'une source, il est plus simple dans l'Inspecteur d'Objet

    • de sélectionner le contrôle et de spécifier quelle est sa source
    • que de sélectionner la source et d'utiliser un dialogue pour désigner tous les contrôles destination
    C'est comme pour les bases de données: un tDataSet peut être lié à plusieurs tDataSources, qui peuvent être liés à plusieurs tDbEdit. Et pourtant nous les relions dans le sens inverse tDbEdit.DataSource pointe vers tDataSource etc)

    Naturellement en interne le composant source possède une liste de ces contrôles pour pouvoir propager les calculs

  • le Structure Treeview est comme pour les applications FireMonkey, beaucoup utilisé. Nous l'avions beaucoup mis a contribution dans l'article sur les styles. De plus la propention à nicher les composants les uns dans les autres (pratique dans certaines circonstances, pénible parfois) rend cet outil indispensable pour remettre les éléments à plat


Plusieurs alternatives à nos étapes:
  • pour créer le premier tBindingsList, nous aurions pu cliquer au bas de l'Inspecteur d'Objet lorsque le contrôle est sélectionné
  • nous aurions aussi
    • directement déposer un tBindingsList depuis la Tool Palette:

      tbindinglist_in_tool_palette

    • pour créer la tBindingExpressions:
         clic double sur BindingsList sur la Forme
         l'Editeur de BindingsList est affiché

      bindinglist_editor

         cliquez sur l'icône jaune en haut à gauche pour ajouter un LiveBinding
         le dialogue New LiveBinding est affiché

      new_livebinding

         cliquez tBindExpression et "Ok"
         BindExpression est ajouté "temporairement" (en grisé dans le Structure Treeview)

      new_bindexpression

         pour attribuer ce BindExpression1 au controle, allez dans l'Inspecteur d'Objet, sélectionnez ControlComponent, ouvrez la combo box et sélectionnez le contrôle
         BindExpression1 se niche dans ce contrôle

      select_bindexpression_control

         documentez les 3 autres propriétés de BindExpresson1
    Beaucoup plus de manipulations que la première solution, mais permet de présenter l'Editeur de BindingsList

  • pour initialiser les valeurs de tBindingExpression, nous aurions pu modifier les valeurs dans le sous-composant de l'Inspecteur d'Objet niché dans control_edit:

    nested_bindexpression_editing

    Pour des raisons de présentation pour cet article, nous avons préféré sélectionner le tBindingExpression seul dans l'Inspecteur d'Objet.



3 - La tBindingsList

Ce composant permet de regrouper plusieurs tLiveBindings. Sa fonction clé est Notify qui permet de provoquer la réévaluation des tLiveBindings qui sont dans la liste.

La syntaxe est:

Procedure TCustomBindingsList.Notify(Const p_c_source_objectTObject;
      Const p_target_propertyString);

  • p_c_source_object est l'objet source
  • p_target_property l'expression qui sera calculée, sous forme d'une String. Nous pouvons fournir la chaîne vide qui notifiera toutes les expressions-cibles liées à CE composant source
A l'évidence, ce Notify là fleure bon le pattern Observer: les éléments à réévalués sont dans une liste, et notifiés lorsque leur source a été modifiée.



3.1 - SourceExpression

L'évaluateur utilise un interprète d'expressions fournies sous forme de String.

Dans notre premier exemple, la source est spinedit_.Value, qui est un Integer, et dans BindExpression1.SourceExpression nous avons mis la String "Value". L'interprète cherche dans son environnement quelque chose nommé "Value", et c'est la propriété de spinedit_.

Le type String permet énormément de flexibilité dans ce qui doit être évalué, mais naturellement le compilateur ne peut vérifier l'expression entièrement. C'est à l'exécution que les expressions incorrectes seront détectées. Ce qui risque d'arriver fréquemment lorsque nous débutons dans l'écriture de ces expressions. Et les messages ne sont pas trop clairs: un peu mieux que "Syntax Error" du Basic AppleSoft, mais pourrait mieux faire ...

Voici quelques exemples. Commençons par calculer une expression qui fournit 2 fois Value avec " euro" comme nom de devise. Si SpinEdit.Value a la valeur 22, nous affichons "44 Euro". Par conséquent :
   démarrez un nouveau projet VCL
   déposez un tSpinEdit, et nommez-le "source_spinedit_"
   déposez un tEdit, nommez-le "control_edit_", et ajoutez-lui un tBindExpression
   dans le Structure TreeView sélectionnez BindExpressionControl_edit_1 et initialisez
  • SourceComponent : source_spinedit_
  • SourceExpression:

      ToStr(Value* 2)+ " euros"

    • Value* 2 calculera le double de la valeur du tSpinEdit
    • ToStr convertit cette valeur en String
    • nous concaténons par "+" la chaîne " euro", qui est délimitée dans notre exemple par le guillemet double (le guillemet simple est aussi utilisable, et nous pouvons jongler avec les deux en cas d'inclusion de chaînes littérales)
  • ControlComponent : control_edit_
  • ControlExpression : Text
   la situation est la suivante:

sourceexpression_tostr

   ajoutez dans source_spinedit_.OnChange le code de notification
   compilez et exécutez. Cliquez sur source_spinedit_ pour provoquer l'évaluation

   voici le résultat:

sourceexpression_tostr_result



Quelques points:

  • il est TRES IMPORTANT de bien vérifier que l'expression que vous tapez dans BindExpression.SourceExpression est bien "envoyée" par l'Inspecteur d'Objet à Delphi. Soit tapez "Entrée" ou, plus fiable, sélectionnez une autre propriété dans l'Inspecteur d'Objet. Si vous vous contentez de taper l'expression, elle restera dans le "in place Edit" de l'Inspecteur d'Objet et ne sera pas prise en compte par le code
  • il faut que l'expression soit évaluable. Si, par exemple, pour saisir 22 nous sélectionnons tout le texte "0" du SpinEdit (pour le remplacer par 22), le texte vide ne pouvant être évalué à un entier, il y aura une exception. Dans ce cas, nous pourrions utiliser:

    Procedure TForm4.source_spinedit_Change(SenderTObject);
      Begin
        If source_spinedit_.Text<> ''
          Then BindingsList1.Notify(source_spinedit_'Value');
      End// source_spinedit_Change



Pour écrire une expression source, il faut donc veiller à la cohérence de cette expression. Nous avons à ce niveau plusieurs fonctions qui permettent les conversions de valeurs. Nous avons déjà utilisé ToStr.

Pour connaître la liste des méthodes utilisables
   sélectionnez BindingsList1
   dans l'Inspecteur d'Objet, sélectionnez Methods, et double-cliquez l'ellipse "..."
   la liste des méthodes est présentée :

bind_27_source_expression_methods_1



Dans cette liste, nous trouvons, par exemple Format. Cette fonction a la même syntaxe que celle de la RTL Delphi.

Nous avons utilisé, par exemple

  • sur un contrôle Edit1, l'expression

      Format('euros : %s ', ToStr(Value) )

    qui donne le résultat suivant:

    source_expression_format

  • sur un contrôle Edit3

      Format('amount %4d ', Value*5 )

    dont le résultat est:

    source_expression_format_2



Ces exemples montrent donc que de nombreuses combinaisons sont possibles, du moment que le résultat peut être raisonablement interprété par un évaluateur de String.

En revanche, nous ne sommes pas arrivés à utiliser des valeurs réelles :

  Format('convert %7.2f ', Value*1,33 )

ou même en utilisant "1.33" ou ToVariant provoque une erreur à cause du "." décimal ou de la "," décimale. Il doit y avoir quelque part une manière de spécifier le séparateur décimal pour l'évaluateur



Les erreurs de l'évaluateur peuvent être assez difficile à localiser. Après avoir vainement essayé de travailler sur des réels, nous avons supprimé le composant Edit3, en supposant que ce essai disparaîtrait. Mais non. En compilant, nous avons eu droit à

could_not_find_text

La raison est que le composant contrôle a été supprimé, mais pas le BindExpression correspondant. En double cliquant BindingsList1, nous avons en effet:

orphan_bindexpression

Il suffit de supprimer cette expression orpheline pour que tout redevienne normal.



Au niveau technique de mise au point

  • il vaut mieux travailler sur un seul couple source / contrôle à la fois
  • l'expression est évaluée lorsque l'exécution est lancée si tBindExpression.AutoActivate est True. Le minimum est que cette première évaluation soit correcte
  • si nous avons déjà mis en place tBindingsList.Notify, il peut être utile de rendre cette évaluation conditionnelle à la valeur d'une tCheckBox pour bloquer les évaluations autres que l'évaluation initiale


3.2 - BindingsList Editor

L'éditeur de LiveBindings affiche aussi dans la colonne "Description" un texte qui spécifie quelle est la source et quel est le contrôle:

bindinglist_editor



En cliquant sur une des lignes, nous ouvrons le dialogue de modification des expressions source et contrôle:
   double cliquez sur BindExpressionEdit21
   le dialogue de modification est affiché

editing_bindinglist

   cliquez sur "Eval Source"
   un dialogue nous affiche le contrôle source et l'expression contrôle, avec le résultat (avec la valeur actuelle de SpinEdit.Value) et son type

editing_bindinglist_expression_result

   donnez à source_spinedit_.Value une valeur autre que 0, par exemple 33
   voici l'évaluation:

editing_bindinglist_expression_result_33



3.3 - Les OutputConverters

Symmétriquement aux fonctions qui nous permettent de modifier la valeur de l'expression source, nous pouvons utiliser des méthodes pour convertir le résultat de l'évaluation. Pour visualiser les possibilités,
   sélectionnez BindingsList1
   sélectionnez OutputConverters et cliquez l'ellipse "..."
   la liste des méthodes est affichée

livebindings_outputconverters




4 - Utilisation de plusieurs Sources

Pour utiliser plusieurs sources dans l'évaluation du résultat, il suffit de déposer un tBindingScope sur la Forme et de désigner ce composant comme SourceControl.

Voici comment calculer un résultat à partir de 2 tSpinEdits:
   créez une nouvelle application VCL
   posez deux tSpinEdit, et donnez des valeurs initiales non nulles (par exemple 3 et 5)
   posez un tBindScope sur la Forme
   posez un tEdit qui affichera le résultat (le "contrôle")
   sélectionnez Edit1, LiveBindings, créez une tExpressionBinding
   dans le Structure Panel, sélectionnez BindExpressionEdit11
   tapez une expression qui utilise les deux tSpinEdits. Par exemple:

  SpinEdit1.Value+ SpinEdit2.Value* 11

   voilà la situation:

tbindingscope

   vous pouvez invoquer l'Editeur de Bindigns en cliquant le lien "Expression" situé au bas de l'Inspecteur d'Objet. Ou, mieux dans ce cas, demander l'évaluation en cliquant le lien "Evaluate"

   la valeur de Edit1.Text prend bien la valeur 58 (3+ 11* 5)

   compilez et exécutez
   la valeur initiale de Edit1 est à nouveau évaluée à 58

   pour provoquer la réévaluation, créez les tSpinEdit.OnChange, et provoquez la réévaluation. Dans notre cas, nous avons créé SpinEdit1.OnChange:

Procedure TForm1.SpinEdit1Change(SenderTObject);
  Begin
    BindingsList1.Notify(Sender'');
  End// SpinEdit1Change

  • Sender référence "celui qui a changé" (pour le moment, SpinEdit1)
  • '' demande la réévaluation de toutes les expressions de contrôle (donc Edit1.Text)
Puis, dans l'Inspecteur d'Objet, nous avons fait pointer SpinEdit2.OnChange vers cet événement

   voici un exemple de résultat

tbindingscope_result



Notez que

  • tBindingScope est un composant intermédiaire utilisé en sous main par l'évaluateur



5 - Evaluation de LiveBindings par code

5.1 - Liaison de 2 strings

La liaison par code n'est PAS nécessaire. Vous pouvez donc sauter allègrement ce paragraphe.



Pour jeter un peu de lumière sur le mécanisme d'évaluation, commençons par lier des String par code.
   créez une nouvelle application VCL
   créez une Classe c_product (la source) ayant une propriété Description
   créez une Classe c_invoice_total (la cible, ou le "contrôle") ayant une propriété InvoiceDescription

Type c_product =
         Class(TObject)
           Private
             FDescriptionString;
           Public
             Property DescriptionString read FDescription write FDescription;
         End// c_product

     c_invoice_total =
         Class(TObject)
           Private
             FInvoiceDescriptionString;
           Public
             Property InvoiceDescriptionString read FInvoiceDescription
                 write FInvoiceDescription;
         End// c_invoice_total

   déclarez deux objets correspondants et créez-les

Var g_c_productc_productNil;
    g_c_invoice_totalc_invoice_totalNil;

Procedure TForm1.create_Click(SenderTObject);
  Begin
    g_c_product := c_product.Create;
    g_c_product.Description := 'computer';

    g_c_invoice_total := c_invoice_total.Create;
  End// create_Click

   pour préparer la liaison, importez les unités nécessaires:

Implementation
  Uses
    System.Bindings.Expression,
    System.Bindings.Helper,
    System.Bindings.EvalProtocol // iScope
    ;

   liez la propriété Description de la source à la propriété InvoiceDescription de la cible

Procedure TForm1.create_bindings_Click(SenderTObject);
  Var l_c_input_binding_associationtBindingAssociation;
      l_c_input_bindingsiscope;

      l_c_output_binding_associationtBindingAssociation;
      l_c_output_bindingsiscope;
  Begin
    // -- associate g_c_product with the string name
    l_c_input_binding_association:= Associate(g_c_product'my_product');
    l_c_input_bindings:= TBindings.CreateAssociationScope(
          [l_c_input_binding_association]);

    // -- associate g_c_invoice with the string name
    l_c_output_binding_association:= Associate(g_c_invoice_total'my_invoice');
    l_c_output_bindings:= TBindings.CreateAssociationScope(
          [l_c_output_binding_association]);

    // -- create an expression binding linking the input and outputs
    TBindings.CreateManagedBinding(
        l_c_input_bindings,
        'my_product.Description',

        l_c_output_bindings,
        'my_invoice.InvoiceDescription',

        Nil);
  End// create_bindings_Click

La méthode TBindings.CreateManagedBinding a 5 paramètres

  • le tableau dynamique des liaisons qui spécifie chacune le nom de l'objet.

    L'instruction suivante appelle la fonction Associate pour lier l'objet g_c_product au nom que nous utiliserons dans l'expression source, "my_product":

    l_c_input_binding_association:= Associate(g_c_product'my_product');

    Voici comment créer la liste de toutes les associations en entrée:

    l_c_input_bindings:= TBindings.CreateAssociationScope(
        [l_c_input_binding_association]);

    Dans notre cas, il n'y a qu'une entrée, mais il pourrait y en avoir plusieurs

  • suit l'expression source
  • puis la même chose pour les sorties
  • enfin un paramètres Nil (la fonction est Overload et nous n'utilisons que la version simple)
   voici un exemple d'évaluation :

Procedure TForm1.evaluate_Click(SenderTObject);
  Begin
    // -- set the value of the Description property
    TBindings.Notify(g_c_product'Description');

    // -- display the result
    display('Invoice Descr: 'g_c_invoice_total.InvoiceDescription'<');
  End// evaluate_Click

   pour voir comment la modification de g_c_product.Description est propagé, placez une tListBox sur la Forme avec quelques description, et évaluez la valeur de la description choisie:

Procedure TForm1.ListBox1Click(SenderTObject);
  Begin
    With ListBox1 Do
      g_c_product.Description:= Items[ItemIndex];
    evaluate_Click(Nil);
  End// ListBox1Click

   voici le résultat de l'exécution

livebinding_by_code



5.2 - Une liaison avec plusieurs Entrées

La mécanique est assez simple, mais le plus dur est de bien voir quel identificateur est utilisé à quel endroit.

Voici un exemple un peu plus complexe avec plusieurs entrées :

  • une Classe c_product a des propriétés Price (395) et Description ('computer')
  • une Classe c_invoice_item représente une ligne d'une facture, avec Quantity (2)
  • nous souhaitons calculer c_invoice_row qui va contenir
    • la quantité
    • la description
    • le prix de cette ligne


Donc
   créez une application VCL
   voici les classes et la création des objets:

Type c_product =
         Class(TObject)
           Private
             FpriceInteger;
             FDescriptionString;
           Public
             Property PriceInteger read Fprice write Fprice;
             Property DescriptionString read FDescription write FDescription;
         End// c_product

     c_invoice_item =
         Class(TObject)
           Private
             FQuantityInteger;
           Public
             Property QuantityInteger read FQuantity write FQuantity;
         End// c_invoice_item

     c_invoice_row =
         Class(TObject)
           Private
             FInvoiceQuantityInteger;
             FInvoiceDescriptionString;
             FInvoiceTotalInteger;
           Public
             Property InvoiceQuantityInteger read FInvoiceQuantity write FInvoiceQuantity;
             Property InvoiceDescriptionString read FInvoiceDescription write FInvoiceDescription;
             Property InvoiceTotalInteger read FInvoiceTotal write FInvoiceTotal;
         End// c_invoice_row

Var g_c_productc_productNil;
    g_c_invoice_itemc_invoice_itemNil;

    g_c_invoice_rowc_invoice_rowNil;

Procedure TForm1.create_Click(SenderTObject);
  Begin
    display('create');
    g_c_product := c_product.Create;
    g_c_product.price := 395;
    g_c_product.Description := 'computer';

    g_c_invoice_item := c_invoice_item.Create;
    g_c_invoice_item.Quantity := 2;

    g_c_invoice_row := c_invoice_row.Create;
  End// create_Click

   les liaisons des 3 valeurs de sortie se fait par:

Procedure TForm1.create_bindings_Click(SenderTObject);
  Begin
    display('bind');
    // -- compute the invoice quantity for this row
    TBindings.CreateManagedBinding(
        // -- inputs
        [TBindings.CreateAssociationScope([
            Associate(g_c_invoice_item'my_invoice_item')
            ])],
        'my_invoice_item.Quantity',

        // -- outputs
        [TBindings.CreateAssociationScope([
            Associate(g_c_invoice_row'my_invoice_row')
            ])],
        'my_invoice_row.InvoiceQuantity',

        Nil);

    // -- compute the invoice description for this row
    TBindings.CreateManagedBinding(
        [TBindings.CreateAssociationScope([
            Associate(g_c_product'my_product')
            ])],
        '" "+ my_product.Description+ " total: "',
        [TBindings.CreateAssociationScope([
            Associate(g_c_invoice_row'my_invoice_row')
            ])],
        'my_invoice_row.InvoiceDescription',
        Nil);

    // -- compute the invoice total for this row
    TBindings.CreateManagedBinding(
        [TBindings.CreateAssociationScope(
          [Associate(g_c_product'my_product'),
           Associate(g_c_invoice_item'my_invoice_item')
           ])],
        'my_product.price* my_invoice_item.Quantity',
        [TBindings.CreateAssociationScope([
            Associate(g_c_invoice_row'my_invoice_row')
            ])],
        'my_invoice_row.InvoiceTotal',
        Nil);
  End// create_bindings_Click

   et l'évaluation se fait par 3 appels successifs à TBindings.CreateManagedBinding. Notez en particulier l'ajout d'espaces dans la description et le calcul du prix total

Procedure TForm1.evaluate_Click(SenderTObject);
  Begin
    // -- if any property of the product change, initialize the properties
    TBindings.Notify(g_c_product'Price');
    TBindings.Notify(g_c_product'Description');

    TBindings.Notify(g_c_invoice_item'Quantity');

    display(
          IntToStr(g_c_invoice_row.InvoiceQuantity)
        + g_c_invoice_row.InvoiceDescription
        + IntToStr(g_c_invoice_row.InvoiceTotal)
        );
  End// evaluate_Click

   voici un exemple de résultat :

livebinding_by_code_several




6 - tLiveBindings et Bases de Données

6.1 - Vcl et FireMonkey

La raison d'être des tLiveBindings est de pouvoir lier les informations venant d'une base de données à des contrôles visuels.

Pour la VCL, nous avons les tDbEdit, tDbGrid etc qui se chargent de ce travail de synchronisation bidirectionnel entre la base et les contrôles de la Forme.

Mais pour FireMonkey, il a été décidé de ne pas créer cette hiérarchie de contrôles sensibles aux données, mais de doter TOUS les composants de LiveBindings.



6.2 - LiveBindings et Base de Données Vcl

Voici un premier exemple avec la VCL:
   créez une application VCL
   posez et initialisez un tDataSet.

Par exemple, posez un tTable BDE et initialisez

  • DataBaseName à DBDEMOS
  • TableName à BIOLIFE.DB
  • Active à True
   posez un tDataSource sur la Forme, et initialisez DataSet à Table1


Pour permettre le LiveBinding des données, nous devons ajouter un tBindScopeDb sur la Forme:
   posez un tBindScopeDB sur le Forme
   initialisez DataSource à DataSource1


Pour afficher un champ dans un tEdit
   posez un tEdit
   sélectionnez LiveBindings, cliquez, confirmez "New LiveBinding"
   dans le dialogue, sélectionnez tBindLink

tbindlink

   dans le Structure TreeView, sélectionnez BindLinkEdit1
   initialisez SourceComponent à BindScopeDB1
   sélectionnez SourceMemberName et ouvrez la combobox
   la liste des champs de la table BIOLIFE s'affichent

vcl_database_livebinding

   sélectionnez un champ, par exemple COMMON_NAME

   la propriété ControlComponent est déjà initialisée
   sélectionnez FormatExpression et double cliquez l'ellipse
   un éditeur de BindLink est affiché

vcl_edit_bindlink

   créez un nouveau "bind link format expression" en cliquant sur l'icône jaune en haut à gauche

   un nouveau tExpressionItem est présenté dans l'Inspecteur d'Objet

vcl_edit_bindlink

   sélectionnez SourceExpression et tapez Value (du champ COMMON_NAME)
   sélectionnez ControlExpression et tapez Text (de Edit1)
   fermez le dialogue d'édition d'item

   procédez de même pour ParseExpression

   pour lancer la première évaluation (et vérifier que nos expressions sont correctes), dans le Structure Treeview, sélectionnez BindLinkEdit11, puis basculez AutoActivate à False, puis à True
   la première valeur de ce champ est affichée (dane le Edit1,

first_live_bindlink

   pour pouvoir nous déplacez dans la table, nous ajoutons un tDbNavigator que nous lions à DataSource1

   voici le résultat de la compilation, après quelques clics sur DbNavigator1:

first_live_bindlink_navigator



6.3 - Affichage d'une image

Pour afficher l'image du poisson dans un tImage
   placez un tImage sur la Forme
   sélectionnez LiveBindings, cliquez, confirmez "New LiveBinding"
   dans le dialogue, sélectionnez tBindLink
   dans le Structure Treeview, sélectionnez BindLinkImage11
   initialisez SourceComponent à BindScopeDB1
   sélectionnez SourceMemberName et séléctionnez GRAPHIC

   ControlComponent est déjà initialisé à la valeur du tImage
   l'éditeur des valeurs cibles est affiché

bindlink_for_timage
cet élément pour ouvrir l'éditeur de cette liaison:

   sélectionnez "Format", ajoutez un élément par l'icône jaune en haut à gauche, et tapez "Self" dans "Source" et "Picture" dans "Control"

   envoyez les valeurs en cochant les marques noire à côté des valeurs saisies (ou en changeant d'item dans la ListBox des collections à gauche)

bindlink_for_timage_items

   procédez de même pour "parse"

   fermez l'éditeur d'items

   dans le Structure Treeview, sélectionnez BindLinkImage11, et dans l'Inspecteur d'Objet, basculez AutoActivate à False puis à True
   l'image est affichée

tdbgraphicfield_in_timage



6.4 - Expressions plus complexes

Voici un exemple d'expression qui ne s'appuie pas directement sur la valeur d'un champ, en affichant le numéro courant de la ligne:
   posez un tLabel sur la Forme
   sélectionnez LiveBindings, cliquez, confirmez "New LiveBinding"
   dans le dialogue, sélectionnez tBindLink
   dans le Structure Treeview, sélectionnez BindLinkLabel11
   initialisez SourceComponent à BindScopeDB1
   laissez SourceMemberName vide

   dans le Structure Treeview, double-cliquez sur BindLinkLabel11 pour ouvrir l'Editeur des valeurs cible
   sélectionnez "Format", créez un nouvel Item, et
  • dans Source tapez

      " Row : " + ToStr(RecNo) + " out of " + ToStr(RecordCount)

  • dans Control tapez Caption (du tLabel)
   voici le résultat:

non_field_expression_1

   validez ces valeurs en cliquant les coches noires (ou en changeant d'item), fermez l'Editeur, resélectionnez BindLinkLabel11 et basculez AutoActivate à False et True pour vérifier les valeurs

   voilà le résultat après quelques déplacements dans BIOLIFE

dataset_livebindings



6.5 - LiveBinding et FireMonkey

Les liaisons aux données pour FireMonkey sont à la fois bien plus simples et bien plus importantes que pour la Vcl. Nous consacrerons un prochain article à cette utilisation des LiveBindings




7 - Télécharger le code source Delphi

Vous pouvez télécharger: Ce .ZIP qui comprend:
  • le .DPR, la forme principale, les formes annexes eventuelles
  • les fichiers de paramètres (le schéma et le batch de création)
  • dans chaque .ZIP, toutes les librairies nécessaires à chaque projet (chaque .ZIP est autonome)
Ces .ZIP, pour les projets en Delphi 6, contiennent des chemins RELATIFS. Par conséquent:
  • créez un répertoire n'importe où sur votre machine
  • placez le .ZIP dans ce répertoire
  • dézippez et les sous-répertoires nécessaires seront créés
  • compilez et exécutez
Ces .ZIP ne modifient pas votre PC (pas de changement de la Base de Registre, de DLL ou autre). Pour supprimer le projet, effacez le répertoire.

La notation utilisée est la notation alsacienne qui consiste à préfixer les identificateurs par la zone de compilation: K_onstant, T_ype, G_lobal, L_ocal, P_arametre, F_unction, C_lasse. Elle est présentée plus en détail dans l'article La Notation Alsacienne



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.



8 - Références

Nos articles liés à Delphi XE2 / FireMonkey
  • Delphi XE2 (Delphi 2012)
      J Colibri : 19 Août 2011
    • présentation de la nouvelle version Delphi XE2 avec les informations disponibles sur Internet le 19 Août. Essentiellement la compilation 64 bit et la nouvelle plateforme FireMonkey pour réaliser des applications exécutables sur Windows 32 bit, Windows 64 bit, Mac OsX et les mobiles iOs (iPhone, iPad etc), ayant en plus un look plus moderne
  • Delphi XE2 - World Tour Paris
      J Colibri - 8 Sept 22011
    • Résumé de ce que nous avons appris à la conférence "Delphi XE2 World Tour" qui s'est déroulée à Paris le 8 Septembre 2011 - Complément de l'article du 19 Août
  • Mise à jour Delphi XE2 1 Tim DelChiaro a annoncé et fourni les liens du téléchargement et de la doc de la première mise à jour Delphi XE2 (28 Septembre 2011)

  • Les Styles FireMonkey
      J Colibri - 11 Sept 2011
    • changer le style d'un objet ou de tous les objets d'une Class, le Style Designer FireMonkey, contenu d'un fichier .STYLE, la propriété StyleLookup, les styles prédéfinis.
  • les animations FireMonkey : choix de la propriété à animer, vitesse, type d'interpolation entre les valeurs début et fin, répétition - Animation 3D. Que choisir: Vcl ou FireMonkey ?
  • Architecture LiveBindings Delphi : analyse de l'architecture des LiveBindings Delphi : comment tBindingExpression compile une expression au format String pour contruire un environment qui référence des tObject et évalue cette expressioin pour remplir les propriétés de composants. Affichage du pseudo-code généré. Diagramme de classe UML de l'architecture des LiveBindings
  • Architecture FireMonkey : la hiérarchie tComponent <- tFmxObject <- Fmx.tControl <- tStyledControl. Diagramme de Classe UML avec les principaux composants et explication de l'emplacements des fonctionalités de base (le Canvas, la Souris, le Clavier).
  • Inspecteur d'Objet simple avec FireMonkey : création d'un Inspecteur d'Objet simple, présentant les composants présents sur la Forme et affichant les noms et valeurs de ses propriété, avec modifications possibles des propriétés à l'exécution. Utilisation de la RTTI, version pré et post Delphi 2010


La documentation Delphi en ligne des Live Bindings se trouve à La racine des exemples Delphi sur SourceForge se trouve à Et les exemples LiveBindings disponibles actuellement sont
  • BindEdits
      LiveBinding entre 2tEdits
        FireMonkey
  • SimpleManagedBindEdits
    Version améliorée de BinEdits
        FireMonkey
  • BindExpression
      utilisation du moteur d'évaluation pour calculer des expressons
        FireMonkey
  • BindList
      data binding avec une tListBox.
        Vcl et FireMonkey
  • Collections
      comment remplire une liste à partir d'une liste custom
        FireMonkey
  • Console
      évaluation d'une expression dans plusieurs versions
      SimpleExpression
      SimpleUnmanagedBindingsConsole - utilise des LiveBindings non managés
      SimpleManagedBindingsConsole - avec notifications
      UnManagedBindingsConsole
      ManagedBindingsConsole
      application Console
  • Exceptions
      traitement des exceptions
        Vcl.
Exemples avec des bases de données :
  • BindLink
      tDataSet tDbNavigator avec un TBindGridLink, lié à des tEdit tImage
        Vcl et FireMonkey
  • BindGridLink
      tDataSet tDbNavigator avec un TBindGridLink, lié à des tStringGrid et autres tEdit tImage
        Vcl.
  • BindDBLink
      un tDataSet / tDbNavigator et un tEdit, tStringGrig,
      FireMonkey
  • BindLookup
      utilisation d'une liste lookup
        Vcl.


Et un article qui était présenté juste avant le lancement (des images écran) :


9 - 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.
Created: jan-04. Last updated: mar-2020 - 250 articles, 620 .ZIP sources, 3303 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 - 2020
Retour:  Home  Articles  Formations  Développement Delphi  Livres  Pascalissime  Liens  Download
l'Institut Pascal

John COLIBRI

+ Home
  + articles_avec_sources
    + bases_de_donnees
    + web_internet_sockets
    + services_web_
    + prog_objet_composants
    + office_com_automation
    + colibri_utilities
    + uml_design_patterns
    + graphique
    + delphi
    + outils
    + firemonkey
      – firemonkey_animations
      – livebindings_delphi
    + vcl_rtl
    + colibri_helpers
    + colibri_skelettons
    + admin
  + formations
  + developpement_delphi
  + présentations
  + pascalissime
  + livres
  + entre_nous
  – télécharger

contacts
plan_du_site
– chercher :

RSS feed  
Blog

Migration Delphi migration de versions Delphi, migration Unicode, migration BDE / base de données, migration Internet - Tél 01.42.83.69.36
Formation Delphi xe3 complete L'outil de développpement, le langage de programmation, les composants, les bases de données et la programmation Internet - 5 jours
Formation Bases de Données Delphi Gestion de bases de données : connexion, accès aux tables, édition d'états - 3 jours