FMX et les composants d'accès aux bases de données

Utilisation des LiveBindings

La chose déstabilisante lorsque l'on aborde la création d'une application multiplate-formes utilisant des bases de données c'est l'absence des composants contrôle de données bien connues des programmes VCL. Dans cet article, je propose de faire le tour de ces composants VCL pour trouver son équivalent FMX, s'il existe !

Bien évidement, la réponse « primaire » est simple : il faut utiliser les LiveBindings. Plusieurs tutoriels, à commencer par les tutoriels d'Embarcadero, et plusieurs vidéos sur le net démontre comment il est aisé de le faire alors pourquoi m'embarquer sur un tel sujet ?

Deux raisons principales :

  • La première les démonstrations se font presque toutes en utilisant le concepteur de liaison visuel.
  • La seconde est que ces démonstrations ne présentent que les composants les plus évident : libellés, zones d'édition, mémos ou contrôles image. Toutefois ces dernières laissent de côté d'autres composants bien utiles, mais d'utilisation plus complexe, comme les boites de choix.

Article lu   fois.

L'auteur

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

I. État des lieux

Lors de mes précédents tutoriels, j'ai déjà introduit quelques types de liaison (en particulier lors de l'épisode précédent). Encore une fois, je le répète, il est possible d'utiliser le codage pour remplir l'interface utilisateur mais si pour un simple libellé une ligne de code suffit, pour une zone de saisie nous passerions déjà à, au minimum, deux lignes ! Je laisse imaginer ce qu'il faut pour remplir une grille ou une vue liste (TListView).

I-A. Composants contrôle de données

La VCL propose quatorze composants contrôles de données listés dans le tableau ci-dessous.

Composant VCL

Équivalent FMX

DBNavigator

BindNavigator

DBText

Label

DBEdit

Edit

DBMemo

Memo

DBImage

Image

DBGrid

Grid,StringGrid

DBCheckBox

CheckBox

DBListBox

Listbox, ListView

DBLookupListBox

ListBox

DBComboBox

Combobox, ComboEdit

DBLookupComboBox

Combobox, ComboEdit

DBRadioGroup

Pas d'ensemble de boutons radio

DBCtrlGrid

Pas d'équivalence

DBRichEdit

Pas de richview

D'expérience, la plupart des besoins d'un programme VCL d'informatique de gestion sont couverts avec ces derniers. Toutefois, les interfaces utilisateurs ont beaucoup évolués depuis, et les nouveaux composants tel TListView n'ont aucun pendant lié aux données (par exemple un TDBListView). Les LiveBindings nous ouvrent de toutes nouvelles perspectives.

I-B. La base de données

J'aurais pu utiliser les bases de données fournies par Embarcadero pour leur incontournable démonstration FishFactory, toutefois elle ne correspond pas totalement à mes besoins futurs de démonstration. J'ai donc choisi une base exemple SQLite dont vous pourrez trouver la structure à cette adresse. À cette dernière, me manquaient une colonne ou une table contenant des images et des descriptions longues (mémo texte). J'ai donc rajouter deux tables, pour ne pas changer la structure des tables existantes dont voici les structures :

table Pochettes
Sélectionnez
CREATE TABLE Pochettes (
idPochette INTEGER PRIMARY KEY AUTOINCREMENT,
idAlbum INTEGER NOT NULL
REFERENCES albums (AlbumId),
Pochette BLOB
);

CREATE TABLE Paroles (
idParoles INTEGER PRIMARY KEY AUTOINCREMENT,
idTracks INTEGER REFERENCES tracks (TrackId),
extraits BLOB
);

Me pardonnerez-vous de n'avoir inclus dans la base de données, fournie ici, que les 10 premières pochettes et seulement les cinq premiers extraits de chansons ?

I-C. Accès aux données

Ce paragraphe s'adresse plus particulièrement aux possesseurs d'une version starter ou professionnelle de Delphi. La version starter ne propose que le composant TClientDataSet comme accès aux données (donc des fichiers au format .xml ou .cds). Pour ceux de la version professionnelle sans le pack Firedac je dois avouer ne pas trop savoir à quels composants base de données ils ont réellement accès.

Toutefois vous avez la possibilité de rajouter (testé avec la version Tokyo Starter 10.2) des composants tiers comme ceux de la suite ZEOSLib et accéder au monde des SGBDRSystème de Gestion de Bases de Données Relationnelles .

Image non disponible
Composants ZeosLib installés

Pour étudier l'exemple proposé en téléchargement, vous devrez donc installer la bibliothèque ZeosLib.

II. Premières expérimentations

Le sous titre de ce chapitre aurait pu être : Comment en arriver là ?

Image non disponible
exécution du programme

Et ce, sans une ligne de code ou presque !

 
Sélectionnez
unit UniteBase;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
  Data.Bind.Controls, System.Rtti, FMX.Grid.Style, FMX.ScrollBox, FMX.Grid,
  FMX.Objects, FMX.Layouts, Fmx.Bind.Navigator, FMX.Edit,
  FMX.Controls.Presentation, FMX.StdCtrls, Data.DB, ZAbstractRODataset,
  ZAbstractDataset, ZAbstractTable, ZDataset, ZAbstractConnection, ZConnection,
  Data.Bind.EngExt, Fmx.Bind.DBEngExt, Data.Bind.Components, Data.Bind.DBScope,
  Fmx.Bind.Grid, System.Bindings.Outputs, Fmx.Bind.Editors, Data.Bind.Grid,
  FMX.Memo;

type
  TForm3 = class(TForm)
    ChinookDB: TZConnection;
    Label1: TLabel;
    Edit1: TEdit;
    ListeLiens: TBindingsList;
    Chansons: TZQuery;
    LiaisonsDB: TBindSourceDB;
    NavigatorBindSourceDB12: TBindNavigator;
    ImageControlPochette: TImageControl;
    LinkControlToField2: TLinkControlToField;
    Ouvrir: TButton;
    Memo1: TMemo;
    StringGridLiaisonAlbums: TStringGrid;
    LinkGridToDataSource1: TLinkGridToDataSource;
    LinkControlToField1: TLinkControlToField;
    Edit2: TEdit;
    Label2: TLabel;
    LinkControlToField3: TLinkControlToField;
    LinkControlToField4: TLinkControlToField;
    ChansonsName: TWideStringField;
    ChansonsTitle: TWideStringField;
    ChansonsPochette: TBlobField;
    ChansonsExtrait: TWideMemoField;
    CheckBox1: TCheckBox;
    ChansonsMediaTypeId: TLargeintField;
    LinkControlToField5: TLinkControlToField;
    BindExpression1: TBindExpression;
    procedure OuvrirClick(Sender: TObject);
  private
    { Déclarations privées }
  public
    { Déclarations publiques }
  end;

var
  Form3: TForm3;

implementation

{$R *.fmx}

procedure TForm3.OuvrirClick(Sender: TObject);
begin
ChinookDB.Connect;
Chansons.Active:=True;
end;

end.

Ma seule concession au code ayant été de gérer l'évènement du bouton "Ouvrir" pour me connecter à la la base de données puis à l'ensemble de données interrogé.

Jusque-là vous pourriez me rétorquer qu'il n'y aurait pas plus de code dans un programme VCL et les composants TDBxxxxxxx. À cela je répondrai qu'il s'agit d'un programme FMX et qu'une petite chose est cachée au niveau de la case à cocher. Les lecteurs qui maîtrisent déjà les liaisons rapides peuvent survoler le chapitre jusqu'à la section II.A.3

II-A. Réalisation

Je vais reprendre les différentes étapes me permettant d'arriver à ce résultat à partir d'une application multi-périphérique (projet FMX) vierge. Il y a différentes manières de faire, j'en propose au moins trois, mais une base commune la partie concernant l'ensemble de données. Donc quelque soit la démarche suivie par la suite, en premier lieu je pose les composants d'accès à mes données :

Un composant TZConnection et un composant TZQuery (ou TZReadOnlyQuery) si je n'ai pas autre chose comme connecteur aux bases de données. Dans le cas contraire leurs équivalents, avec Firedac il s'agit d'un TFDConnection et un TFDQuery (plus proche que cela c'est difficile non ?).

Je vais aussi poser sur ma forme un composant TBindingsList.

Pour ce dernier, seul les possesseurs d'une version starter seront obligés de le poser à cet instant pour pouvoir accéder aux liaisons. Idem pour ceux qui voudront utiliser les experts de liaisons sans passer par la case « pose des composants visuels ».

Image non disponible

Pour des raisons pratiques évidentes dès que l'on a affaire à plusieurs ensemble de données, n'oubliez pas de renommer les composants.

Je ne m'étendrai pas sur les propriétés à indiquer au niveau des composants de connexion, partie plus ou moins hors sujet, juste une indication sur la requête nécessaire à la récupération des données.

 
Sélectionnez
SELECT T.Name,T.MediaTypeId,A.Title,P.Pochette,S.extrait  FROM TRACKS T
            JOIN ALBUMS A ON T.Albumid=A.Albumid 
            LEFT JOIN POCHETTES P ON A.ALBUMID=P.IDALBUM
            LEFT JOIN PAROLES S ON T.TrackId=S.idTracks

J'active ensuite l'ensemble de données (ce qui me connecte aussi à la base de données) afin d'avoir tout de suite accès à la structure et aux valeurs des colonnes de ma requête.

Ne pas oublier qu'il s'agit d'une base SQLite et donc à accès unique. L'ouverture de la connexion bloque donc la base à tout autre programme. Il ne faudra pas oublier de déconnecter le composant de connexion par la suite.

II-A-1. Première méthode : utiliser l'expert de liaison

Après avoir sélectionné le composant TBindingsList, en bas de l'inspecteur d'objet un menu contextuel propose deux options :

  • Liaison des composants...
  • Nouveau composant Livebindings...
Image non disponible

Pour cette méthode je choisis la première option qui m'ouvre l'éditeur de lien.

Un double-clic sur le composant fait la même chose.

En cliquant ensuite sur le bouton nouveau un menu à trois options apparaît :

  • Nouvelle liaison... Inser
  • Expert LiveBindings... Maj+Inser
  • Nouveau xxxxxxxxxx... Ctrl+Inser (dépendant de la dernière liaison créée)

Je choisis alors la seconde option.

Les touches raccourcis, faciles à mémoriser, vous feront gagner du temps.

Il suffit ensuite de se laisser guider. J'ai choisi en premier lieu de poser une grille.

Image non disponible
Image non disponible
Image non disponible
Image non disponible

Luxe, la dernière étape de l'expert me permet même de rajouter un navigateur, je m'empresse donc de cocher la case avant de terminer. J'obtiens alors les différents composants demandés avec, en plus un composant TBindSourceDB que je réutiliserai par la suite.

Image non disponible

Le tout est centré dans la forme reste alors à les déplacer, retailler, renommer et dans le cas de la grille modifier les colonnes.

Je peux dès lors répéter ces opérations pour ajouter d'autres composants, par exemple un TEdit.

Image non disponible
Image non disponible

Profitez en au passage pour voir la richesse du nombre de contrôles disponibles. Je vous ferais remarquer en particulier le TDateEdit qui n'a pas d'équivalent TDBDateEdit en VCL hors ajout de composants tiers ou encore tout ce qui concerne les couleurs.

Image non disponible

Prenez soin de sélectionner le BindSourceDB1 déjà créé et non l'ensemble de données « Chansons » sous peine d'obtenir un nouveau composant TBindSourceDB.

Image non disponible
Image non disponible

Encore un petit luxe, le dernier dialogue de la série propose d'ajouter un libellé au contrôle, ce que je fais. J'obtiens alors deux nouveaux composants un TEdit et un TLabel enfant.

Très bonne petite astuce que ce TLabel enfant du TEdit car cela permet de déplacer les deux contrôles en même temps lors du design. On n'y aurait peut-être pas pensé en posant les composants avant de faire les liaisons !

Image non disponible

Inconvénient de cette méthode, les composants sont « en vrac », on imagine facilement ce que un nombre important de contrôles tous centrés peut engendrer comme confusion ! La démarche n'est donc pas très naturelle, en général on préférera d'abord dessiner l'interface. J'ai surtout présenté cette méthode pour la grille, la vision des composants qu'il est possible de lier et la petite astuce du TLabel enfant.

II-A-2. Deuxième méthode : préparer l'interface utilisateur

Cette seconde approche est certainement plus naturelle. Je pose tout d'abord tous les composants dont j'ai besoin avant de passer à la phase liaison.

Image non disponible

À ce stade les possesseurs d'une version intégrant le concepteur de liens visuel ont un avantage certain puisque, en gros, il suffit d'opérations de glisser-déposer pour créer les liens.

Surtout, n'oubliez pas de nommer vos composants (en particulier ceux que vous allez lier) car, avec des noms comme Edit1, Edit2, Edit3 vous aurez quelque mal à savoir avec quel champ vous voulez faire la liaison !

II-A-2-a. Avec le concepteur visuel

Un simple clic droit sur la forme permet d'obtenir le menu contextuel, en sélectionnant l'option « Lier visuellement... » j'arrive alors dans mon concepteur. Glisser et déposer les éléments pour raccorder les propriétés entre elles permet d'obtenir toutes les liaisons nécessaires.

Image non disponible

II-A-2-b. Sans le concepteur visuel

Avec la version Standard, point de concepteur ni de menu contextuel ,mais ce n'est guère plus compliqué que pour la première méthode. En effet l'expert, appelé par un double-clic sur le TBindingList, présente, lors de la seconde étape du dialogue, les contrôles adéquats (selon la première étape) existants.

Le processus est donc quasi identique que pour la première méthode présentée. La différence ? Minime, une étape en moins dans le déroulement de l'expert portant essentiellement sur ce que j'ai présenté comme étant le « petit luxe supplémentaire ».

En suivant les mêmes étapes que dans la partie II.A.1

Image non disponible
Choix du type de contrôle
Image non disponible
Sélection de la grille existante
Image non disponible
Choix de la source de données

Même principe pour les contrôles existants.

Image non disponible
Liaison a un contrôle
Image non disponible
Choix du contrôle

Vous remarquerez ici que ma suggestion de ne pas oublier de nommer mes contrôles explicitement s'explique. Edit1, Edit2, quelle zone de saisie correspond à quel champ !

Une fois les différentes liaisons faites, j'obtiens cette liste

Image non disponible

Encore une fois, je ne peux que recommander de renommer les liens plus explicitement.

Il me manque la liaison avec le navigateur. Pour ce dernier, point besoin d'expert, le remplissage de la propriété DataSource avec la valeur LiaisonsDB (nom que j'avais donné à mon composant TBindScopeDB fera le nécessaire.

Une minute, mais quand donc ce composant est apparu ? C'était pour voir si vous suiviez ! Plus sérieusement, tout à ma démonstration j'ai repris ce qui avait déjà été fait lors de la première méthode, et supprimer tous les liens existants dans la liste afin de re-dérouler l'expert. Mais ce composant est resté sur ma forme.

Pas d'expert pour ça, faudrait-il alors déposer le composant sur la forme et remplir sa propriété DataSource (avec la valeur Chansons dans mon exemple), et ce avant toute autre chose ? En fait, non, lors de la liaison de la grille avec une source de données, troisième image de ce chapitre, seul Chansons apparaîtra. Le fait de sélectionner cette source, l'expert fera alors la création nécessaire.

L'option « Lier le contrôle à un champ » ne fait pas apparaître tous les composants disponibles, uniquement ceux qui peuvent être bi-directionnels.

Pour lier les autres vous sélectionnerez l'option « Lier une propriété de composant à un champ ».

II-A-3. La méthode des « experts »

Jusqu'à présent je n'ai utilisé que des liaisons classées dans la catégorie liaisons rapides. Généralement cette catégorie est suffisante, toutefois ce type de liaison est quelquefois pénalisante par exemple, lorsque l'on veut vérifier la syntaxe des expressions, il est impossible d'accéder à cette possibilité.

C'est là où intervient l'option du menu « Nouveau composant Livebindings... » en bas de l'inspecteur d'objets ou de l'option « Nouvelle liaison ... » de l'éditeur de TBindingsList. Un choix de type de liaisons est alors proposé.

Image non disponible

Ignorez les liens DB puisque obsolètes, faites abstraction des liens rapides autant passer par les autres méthodes qui les créent, le champ des possibles se limite alors dans l'arborescence aux Liens et Listes

Pourquoi et comment passerai-je alors par ce dialogue ? Le pourquoi est déjà exposé, le plus souvent il s'agit de pouvoir vérifier les expressions utilisées dans la propriété CustomFormat. Un mauvais remplissage de cette propriété et, avec de la chance, rien ne s'affiche mais, au début de mes confrontations avec les LiveBindings, cela pouvait aller jusqu'à un plantage de l'EDI !

II-A-3-a. Un TLabel

Comme de juste, le plus simple en premier ! J'ai pris un TLabel jusqu'alors non introduit mais cela aurait pu être tout autre composant avec une propriété Text : un TPanel, un TButton, etc.

Pour l'exemple, qu'en serait-il si, au lieu d'utiliser des zones de saisies pour afficher le titre de la chanson et son album, je voulais afficher des données via une simple zone texte et si de surcroît je voulais que cela soit écrit en majuscules ?

Pour tester, j'ajoute à l'interface utilisateur un TLabel que je nomme « LibelléLié »

Bien sûr, il y a toujours le possibilité code, en codant l'événement AfterScroll par exemple.

 
Sélectionnez
procedure TForm3.ChansonsAfterScroll(DataSet: TDataSet);
begin
LibelléLié.Text:=UpperCase(Chansons.FieldByName('Name').AsString);
end;

Toutefois l'utilisation de cet événement peut s'avérer gênant pour plein de raisons, la première étant que même si le contrôle est désactivé l'instruction s'exécutera.

L'expression étant simple, une liaison rapide (TLinkPropertyToField) suffirait également pour peu de remplir sa propriété CustomFormat correctement. Mais justement, et c'est là que la bât blesse lorsque l'on débute, quelle est la bonne expression ? Impossible à tester avec une liaison rapide, ça passe ou ça casse.

Quel type de liaison choisir alors ?

Liens

 

TBindLi nk

Conçu pour définir un lien permettant de transmettre la valeur de la propriété d'objet spécifiée à une source de données sélectionnée.

TBindListLink

Un lien spécialisé pour les objets List.

TBindGridLink

Un lien dédié pour les objets Grid

TBindPosition

Lien qui permet de synchroniser les positions entre les objets travaillant avec des collections (telles que ListBox et Grid)

TBindControlValue

Lien qui permet de synchroniser les valeurs lorsqu'elles changent dans l'objet source.

Listes

 

TBindList

Définit une liaison unidirectionnelle pour remplir des objets tels que ListBox, Combobox, etc.

TBindGridList

Définit une liaison unidirectionnelle pour remplir les objets Grid.

Tableau : Types de liaisons

Un TBindLink fait donc l'affaire.

Image non disponible

Une fois sélectionné, il me faut remplir les propriétés ControlComponent, SourceComponent et SourceMember.

Image non disponible

Malgré ces renseignements, rien ne se passe comme cela se faisait avec les liaisons rapides. Il va me falloir aussi remplir les expressions. Si l'on veut tester les expressions le mieux est, sans nul doute, de double cliquer sur le lien dans la liste des liens pour obtenir une fenêtre de modification de lien.

Image non disponible
Image non disponible

Je demande à ajouter un formatage pour pouvoir indiquer quelle propriété du contrôle je vais modifier (ici Text), je peux alors facilement vérifier la valeur et le type de celle-ci avec le bouton « Évaluer le contrôle ». Je ferai de même pour la source, c'est d'ailleurs là que je vais utiliser mon expression, en bref que j'écrirai mon code.

Image non disponible

Cette fois, non seulement j'ai pu évaluer l'expression mais utiliser le bouton « Assigner au contrôle » va changer la valeur.

À la place de Value je peux utiliser Text, %s ou encore DisplayText.

Ma préférence va à Value qui fait référence à la valeur du champ qui sera transformé en chaîne. Les trois autres, différence subtile, sont la représentation en chaîne de la valeur.

Pour une colonne texte cela a peu d'importance, pour une colonne numérique par contre … Si les anglo-saxons utilisant le point comme séparateur de décimales n'auront aucun problème pour une expression comme 1*1.25, pour les utilisateurs de la virgule le message « ne peut calculer 1*1,25 » fait rire jaune.

Image non disponible

Pour quelque chose d'aussi « simple » il n'y a pas de quoi sauter d'excitation ! Mais je vais pousser le bouchon un peu plus loin. Si, toujours pour l'exemple, je voulais que la couleur du texte soit rose si le type de média était du MPEG (MediaTypeId=1) ?

Il m'est tout à fait possible de rajouter une autre expression de format pour ce lien, il s'agit juste d'indiquer la propriété que je veux affecter (TextSettings.FontColor)

Image non disponible

et de fournir un expression correcte pour ce qui est de la source.

Image non disponible

Mon premier essai peut se vérifier, quelques tâtonnements plus tard, erreur corrigée, j'ai le résultat souhaité.

Image non disponible

Quelques notes à propos de Dataset.FieldByName('MediaTypeId').Value.

Plusieurs autres écritures sont tout aussi valables :

Dataset.MediaTypeId.Value,

DataSet.MediaTypeId.Text,

Owner.DataSet.MediaTypeId.Value,

Self.DataSet.MediaTypeId.Value,

etc.

Ce n'est qu'une vieille habitude, héritée de la programmation d'avant les LiveBindings, qui me fait utiliser la première forme.

Image non disponible

Quels sont les chiffres mis ? Le premier (-60259) correspond à la couleur rose, le second (-16777216) au noir.
Comment les ai-je obtenus ? Simplement en changeant la propriété TextSettings.FontColor dans l'inspecteur d'objets puis en utilisant le bouton qui permet d'évaluer le contrôle.
Le problème le plus complexe a été de pouvoir accéder au champ MediaTypeId alors que l'on partait du membre Name de la source.

Un TBindLink nous ouvre donc des perspectives non offertes par une liaison rapide.

Qu'en est-il s'il s'agissait non pas d'un texte classique mais d'une date ou d'un chiffre quelconque (flottant, monétaire, etc.) que je voudrai formater selon mes goûts ? Deux solutions :

- Soit j'ai déjà au préalable indiqué le format au niveau de mes déclarations de champs (propriété DisplayFormat du TField) auquel cas je peux utiliser DisplayText au lieu de Value (plus parlant pour moi) ou Text utilisé précédemment pour l'expression de la source.

Image non disponible

Dans l'image ci-dessus, j'ai utilisé DisplayText pour afficher la date selon mon DisplayFormat. Pour le champ Total, j'ai indiqué qu'il était de type monétaire (propriété currency du champ à True)

- Mais il est certainement plus simple de ne pas avoir à déclarer les champs et leur format et utiliser directement une expression de Format (FormatDateTime en cas de date) incluse dans le moteur des LiveBindings.

II-A-3-b. Un TEdit

Pour une zone de saisie, même raisonnement et même type de lien que pour un TLabel. Bien sûr, il est inutile de jouer sur la casse puisque le composant TEdit permet de le faire. Par contre, jouer avec la propriété CustomFormat peut amener quelques problèmes.

Par exemple : je peux très bien afficher le nom complet d'un client

Image non disponible

Ne pas indiquer d'expression ParseFormat empêche la liaison d'être bidirectionnelle. Toute saisie faite dans la zone d'édition ne sera pas reportée dans la base de données.

Pour reprendre mes exemples de date et monétaire du paragraphe précédent.

Image non disponible

La date affichée sous la forme jj/mm/aa ne pose aucun problème par contre si ma date avait la forme jour, nom du mois, année (dd mmmm yy ou dd mmm yy) il me faut passer par une conversion du texte saisi dans une expression permettant d'analyser cette valeur (ParseFormat). La méthode de transformation de chaîne en date StrToDateTime fournie dans les méthodes « usine » semble être la solution.

Image non disponible

Toutefois, en utilisant le bouton d'évaluation ce n'est pas le cas !

Il faudra définir sa propre fonction de conversion. Ce sujet a déjà été abordé dans les tutoriels de l'épisode 2 (parties a et b)

LiveBindings : Les effets de bord

Livebindings : L'évaluateur d'expressions.

II-A-3-c. Un TGrid

Pour lier une grille, bien évidemment, je passe par le lien spécialisé TBindGridLink (cf ). Cette partie est réellement complexe, tellement que je préfère botter en touche. Pour s'en convaincre, il suffit de regarder le nombre d'expressions qu'il faut mettre en place en regardant en détail une liaison faite selon l'un des deux méthodes précédentes.

Image non disponible
Image non disponible
Gestion des positions

À n'en pas douter, vous ferez comme moi et passerez par l'expert, quitte par la suite à modifier quelques expressions selon les besoins. Si toutefois vous voulez persister dans cette voie, je vous recommande la lecture de ce document Understanding Rad Studio livebindings de Cary Jensen

II-B. Mention particulière pour la case à cocher

La case à cocher amène au premier cas particulier, celle-ci attend une valeur booléenne que je n'ai pas dans mes données. Je vais me donner un petit coup de pouce et décide que la case sera cochée si et seulement si la valeur de la colonne MediaTypeId correspond à la valeur 1 c'est-à-dire un fichier média MPEG.

J'aurais pu choisir d'indiquer via cette case d'autres informations par exemple : fichier audio ou pas, fichier protégé ou non, etc. Regardez dans la table Media_Types pour avoir une idée des possibilités.

J'utilise alors la propriété CustomFormat du lien afin de coder le comportement de la case.

IfThen(value=1,True,False)

La casse de la propriété est très importante, lire mon tutoriel sur l'interpréteur d'expressions pour plus d'informations.

Bien, mais qu'en serait-il si je devais modifier une colonne ? Je vous demande un peu d'imagination car la base de données Chinook ne semble pas contenir de colonne booléenne ou une colonne ne pouvant contenir que deux valeurs, par exemple 'O' ou 'N'.

Dans ce cas, j'utilise ces expressions :

Propriétés du lien

Expression

CustomFormat

IfThen(value='O',True,False)

ParseFormat

IfThen(True,'O','N')

III. Les Listes : TListBox, TListView

Les remplissages de listes avec les LiveBindings sont déconcertants de simplicité. Sans une ligne de code un remplissage simple se fait très rapidement, des astuces que j'ai déjà dévoilées dans d'autres tutoriels (Livebindings: Données sans SGBD,Mettre de la couleur dans une ListView) permettent ensuite de customiser la présentation.

La seule différence par rapport à ces deux tutoriels réside dans le fait que l'on ne lie plus des objets internes au programme via un TListLinkControlToField mais une source de données pour laquelle on utilisera plutôt un lien TLinkFillControlToField.

Encore une fois, ceux qui ont une version professionnelle ou plus seront avantagés grâce au concepteur visuel. C'est d'ailleurs grâce à ce dernier que j'ai pu détecter que le type de lien était différent du remplissage fait lors de mes deux précédents tutoriels. Dans le cadre de ce tutoriel je ne me vois pas débattre de l'utilisation de l'un plutôt que l'autre, je préfère illustrer l'utilisation de ces listes.

III-A. Utilisation sur l'ensemble de données principal

On pourrait très bien remplacer la grille, aux données modifiables, par une liste.

Dans ce cas le lien sera de type TListLinkControlToField.

Image non disponible

Ci-dessous, un simple TListBox, toujours rempli sans une ligne de code, que l'on peut facilement améliorer pour y ajouter recherche et regroupement (ici, regroupement par album).

Utiliser le regroupement donne des résultats assez désastreux au niveau de la synchronisation avec les autres zones d'affichage sur la même source de données. Bogue ou mauvaise liaison de ma part, je n'ai pas encore tranché, j'ai l'impression que le positionnement est géré par l'index de l'élément et non par sa valeur.

III-A-1. Lier un TListBox avec le concepteur visuel

Toujours par simple glisser-déposer je synchronise tout d'abord la liste à ma source de données, puis j'indique quel champ servira au remplissage de ma liste. Je crée le schéma suivant.

Image non disponible

Rajouter le groupement se fait aussi simplement, il faut toutefois s'assurer de bien rajouter un TListBoxGroupHeader à la liste avant de procéder à la liaison.

Image non disponible

III-A-2. Lier un TListBox sans le concepteur visuel

Avec l'habitude, c'est presque moins frustrant à utiliser que le concepteur visuel surtout sur un poste de travail un peu lent. Je procède toujours de la même manière, c'est-à-dire avec un double-clic sur le composant conteneur de liaisons : TBindingsList et l'ajout d'un TListLinkControlToField. Il suffit alors de remplir les quelques propriétés nécessaires pour obtenir le même résultat qu'avec le concepteur visuel.

Image non disponible

Surligné en jaune, les propriétés nécessaire pour la liste, les points rouges indiquent les propriétés nécessaires pour un regroupement (ici par titre d'album).

III-A-3. Ajout d'un système de recherche

Aucune liaison n'est nécessaire, il suffit de rajouter un élément TSearchBox au contrôle via le menu contextuel (clic droit sur le contrôle de liste).

III-A-4. Opter pour un TListView

Image non disponible

Ci-dessous un exemple de ce que l'on peut faire

C'est loin d'être parfait, à cause de mes choix de StyleBook, pour ce type de composant le style Transparent sera plus adapté. Toutefois, à contrario du TListBox, regroupements comme recherches ne posent aucun problème de synchronisation.

Image non disponible

Le processus de liaison est identique à celui précédemment indiqué pour le TListBox.

Les « secrets » de l'apparence dynamique, vous les retrouverez dans mon tutoriel : Mettre de la couleur dans un TListView.

III-B. Utilisation sur une table « référence »

Par exemple pour avoir une liste des types de média (table media_types) et mettre en exergue le libellé correspondant en fonction de la valeur de la colonne media_type de la requête Chansons.

Utilisation d'un lien de type TLinkFillControlToField.

Image non disponible
debutzoom[F:\Livebindings\Article 5\Images\ListesUI.PNG]Version Firedacfinzoom

En haut de l'image un TListBox, en bas un TListView. À l'exécution l'élément est sélectionné.

Image non disponible

Bien sûr les modes de recherche dans les listes et même les regroupements étant possible.

III-B-1. Méthode « manuelle »

Une fois n'est pas coutume, c'est par la méthode « manuelle » que je commence mon explication. Cette fois-ci j'ajoute un TLinkFillControlToField à mon conteneur de liens. Toutefois, avant de procéder, je me dois de rajouter un source de données (un composant table pointant sur media_type) et un moyen pour y accéder (un TBindScope).

Je vais, une fois de plus, ne jouer que sur les propriétés de ce lien pour avoir mon remplissage et ma liaison avec la source de données principale.

Image non disponible

Les points importants sont marqués par un point rouge. Ce qu'il faut comprendre c'est que le Datasource fait référence à la source de données principale (Chansons), et que le champ qui fait la liaison (FieldName) est la colonne MediaTypeId de celle-ci. Le FillDataSource, comme tout ce qui commence par Fill d'ailleurs, concerne la source de données référence (table media_types). FillDisplayName indique quelle colonne sert à l'affichage, FillValueFieldName la colonne de référence (foreign key) de la table.

III-B-2. Avec le concepteur visuel

Le piège, si tant est que l'on puisse parler de piège, réside dans la tentation d'utiliser synch comme dans le chapitre III.A. En fait on doit construire le schéma suivant :

Image non disponible
debutzoom[F:\Livebindings\Article 5\Images\ListeConcepteur.PNG]Listes référencesfinzoom

Vous remarquerez que le schéma est le même qu'il s'agisse d'un TListBox ou d'un TListView.

IV. Les Boîtes de choix : TComboBox, TEditComboBox

Dans des bases de données classiques on a souvent à faire à des colonnes faisant références à d'autres tables connexes, ce qui est indiqué par des relations de clés étrangères (FOREIGN KEY). La table Tracks de la base de données exemple (chinook.db) en est une représentation convaincante.

Image non disponible

L'objectif est donc de montrer sur l'interface utilisateur une liste de choix possibles et, de préférence, compréhensibles.

IV-A. TComboBox

Sans conteste, c'est le contrôle qui ressemble le plus au TDBLookupComboBox de la VCL lorsqu'il est correctement lié aux données. Sa prise en main par contre a été dans mon cas difficile, une question de termes à comprendre, en fait je n'avais pas fait attention à quelque chose de tout bête : ce n'est ni plus ni moins qu'une liste améliorée au niveau de l'interface graphique mais réduite à sa plus simple expression.

IV-A-1. Avec le concepteur

Les possesseurs des versions ayant le concepteur de liens visuels seront fortement aidés pour cette partie. Une fois la source de données référence posée voilà la représentation de ce qu'il faut obtenir par des opérations simples de glisser-déposer.

Image non disponible

Ce qu'il faut comprendre :

  • SelectedValue lie la colonne MediaTypeId à la source Chansons (la table que je veux modifier) cette valeur est recherchée ou renseignée via la source de données Médias par l'intermédiaire de Item.LookUpData.
  • Item.LookUpData permet de chercher ou renseigner la valeur MediaTypeId de la source de données Médias.
  • L'affichage est fait par l'intermédiaire de Item.Text, ici lié à la colonne Name de la source de données Médias.

Évident et simple une fois que le principe est explicité. Pour faire la même chose sans le concepteur visuel de liaisons c'est beaucoup moins facile !

IV-A-2. Méthode « manuelle »

Tout d'abord quel type de lien choisir ? J'ai fait l'apologie de la non-utilisation des liaisons rapides afin de pouvoir tester les expressions, hélas il n'y a aucune liaison autre qu'une liaison TLinkFillControlToField pour faire ce que je veux.

Premièrement je dépose un composant d'accès à ma table (ZTable,ZQuery ou ZReadOnlyQuery ; FDTable ou FDQuery), dans le cas de la table Tracks l'accès à la table entière est parfait. Ensuite je dépose un composant TBindScopeDB que je relie ,grâce à sa propriété Dataset, à ma source de données (ZTable ou FDTable) renommée Médias plus de facilités de lecture.

Image non disponible

Malheureusement ce BindScopeDB ne se crée pas automatiquement, faites attention à bien le créer avant l'étape suivante.

Je double-clique ensuite sur le contrôle TBindListLink pour accéder aux experts de liaisons et j'ajoute une nouvelle liaison TLinkFillControlToField. Tout se joue ensuite sur le remplissage des propriétés de ce lien.

Image non disponible
Image non disponible

Propriété

Valeur à mettre

Control

Le contrôle combobox que je veux lier.

DataSource

La source de données principale, c'est-à-dire celle où je veux faire à l'occasion une modification.

FieldName

La colonne concernée.

FillDataSource

La source de données qui va remplir la liste de la boite de choix.

FillDisplayFieldName

La colonne qui contient les données à afficher dans la liste.

FillValueFieldName

La valeur que je veux récupérer.

C'est fait, malheureusement au moment du design rien n'est visible dans la boîte de choix, il faudra attendre une exécution pour voir si le lien fonctionne correctement.

IV-B. TComboEdit

De mon point de vue, le nom de ce composant induit en erreur. Je m'attendais à la même chose que le TComboBox mais avec une possibilité de saisie supplémentaire. C'est en partie vrai, mais il semble impossible d'utiliser, sans grandes astuces, le même système de couple libellé-valeur. Il sera principalement à utiliser pour des colonnes avec tables connexes mais où la valeur de référence sert également de libellé.

Par exemple, je récupère via une requête les valeurs distincte d'une colonne Civilité d'une table Clients et je les utilise comme éléments de choix. Pour peu de rafraîchir régulièrement la requête, j'aurais toutes les valeurs en cas de saisie d'une nouvelle. Si dans ma table Clients je n'ai que des personne physiques francophones je me retrouverai très certainement avec les choix M., Mme, Mlle. Imaginons que je rajoute un client anglophone, peut être alors mettrai-je Mr. Un rafraîchissement de ma requête et le choix sera alors complété.

Pour illustrer cet exemple, j'ai rajouté une colonne à la table customers de la base. Plutôt que de le faire directement dans la base de départ, j'ai préféré le faire par code.

Avec Firedac
Sélectionnez
procedure TForm1.ConnexionClick(Sender: TObject);
var CiviliteExist : Boolean; // colonne cvilité existe ? 
begin
CiviliteExist:=False;
ChinookDB.Connected:=True;
// utilisation des métadonnées pour détecter l'existence de la colonne
FDMetaInfoClients.Active:=True;
while not FDMetaInfoClients.EOF  do
 begin
   CiviliteExist:=CiviliteExist OR (CompareText(FDMetaInfoClients.FieldByName('COLUMN_NAME').AsString,'civilities')=0);
   FDMetaInfoClients.Next;
 end;
FDMetaInfoClients.Active:=False;
// Ajout au besoin de la colonne
if not civiliteexist then ChinookDB.ExecSQL('ALTER TABLE customers ADD civilities CHAR(5)');
// réinitialisation de la colonne à chaque ouverure pour les tests
ChinookDB.ExecSQL('UPDATE customers SET civilities=NULL');
FDQCivilites.Active:=True;
FDTableClients.Active:=True;
end;

J'ai créé une nouvelle fiche pour interroger et modifier ma table customers.

Image non disponible
debutzoom[F:\Livebindings\Article 5\Images\ComboEditDesign.PNG]Utilisation avec Firedac finzoom

La gestion de l'événement AfterPost de la table va me permettre de remplir la boite de choix en cas de nouvelle saisie.

Avec Firedac
Sélectionnez
procedure TForm1.FDTableClientsAfterPost(DataSet: TDataSet);
begin
if ComboEditCivilité.Items.IndexOf(FDTableClients.FieldByName('Civilities').AsString)<0 then
 begin
   FDQCivilites.Active:=False;
   FDQCivilites.Active:=True;
   FDTableClients.RefreshRecord; // nécessaire
 end;
end;

Plusieurs possibilités pour la requête permettant de recenser les civilités utilisées, j'ai opté pour un simple SELECT DISTINCT civilities FROM customers ORDER BY civilities .

La seule contrainte est qu'il faut faire un rafraîchissement de l'enregistrement en cours pour que la boite de choix réagisse correctement.

Pour l'ordre de la liste, une autre solution : déclarer la colonne comme indexée (propriété IndexFieldNames)

IV-B-1. Liaison avec le concepteur visuel

C'est avec de dernier que de la pauvreté de TComboEdit par rapport au TComboBox est la plus criante. Seuls deux membres sont accessibles : SelectedValue et ItemText.

Image non disponible

V. En cas d'erreur

Oui, cela arrivera, aussi bien avec le concepteur visuel que sans. Deux cas sont possibles :

  • Une erreur dans l'IDE, message peu sympathique , indiquant qu'un paquet ne peut plus être chargé.
  • Une erreur à l'exécution avec une adresse pas toujours explicite.
Image non disponible

Pas de panique, en général la suppression d'un lien suffit à régler la situation.

VI. Conclusion

Je me suis attaché aux liaisons, sans me soucier de la partie bidirectionnelle de celles-ci. C'est un choix assumé car plus un problème d'utilisation des composants base de données que de liaison au sens-strict, dépendant donc du choix des composants d'accès aux données (utiliser une table rend la chose facile, utiliser une requête telle celle proposée dans le programme d'illustration rendra obligatoire l'ajout d'un composant UpdateSQL, ou équivalent).

À propos des composants d'accès aux bases de données, je m'excuse d'avoir utilisé FireDac ou ZEOSDBO indifféremment pour mes illustrations. La faute en revient au fait que mes tests de la version Starter ne sont pas fait sur le même poste de travail. Néanmoins cela n'a que peu d'impact, l'important étant l'utilisation du BindScopeDB à chaque fois.

Que conclure de cette technique utilisant les LiveBindings ? Si l'on se contente de faire une comparaison avec les composants VCL d'accès aux données, que j'aurais tendance à utiliser pour des programmes Windows, seul le remplissage de listes est, à première vue, le point positif. L'utilisation des LiveBindings dans un programme VCL prend plus de sens si l'on utilise les fonctionnalités CustomFormat et ParseFormat que je n'ai que très légèrement abordé. Pour les programmes multiplate-formes, donc FMX, la question ne se pose pas puisque c'est la seule solution. Cependant il est possible, , grâce aux Livebindings, de lier bien plus de composants, et pas uniquement ceux présentés.

Ce qui est regrettable c'est que l'utilisation de cette technique, évitant beaucoup de codification, ne laisse aucune place à la documentation technique du programme nécessaire par la suite.

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2017 Serge Girard. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.