FMX : TListView, apparence dynamique et image

Serge Girard

L’objectif est d’afficher une icône selon l’état d’une donnée dans un TListView. Il existe plusieurs apparences prédéterminées (propriété ItemAppearance) qui permettent cela. En fait toutes celles dont le nom commence par Image. Le mode d’apparence dynamique (DynamicAppearance) n’offre pas cette possibilité. Le but de ce petit article est de montrer comment réaliser la même chose avec un peu de code.

Commentez

- Titre : FMX : TListView, apparence dynamique et image

- Auteur : Serge Girard

- Parution : 7 juillet 2020

- Public visé :

- Licence : 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 ® 2019 Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Voir également :

- Mettre de la couleur dans un TListView

- Personnaliser un TListView : ajouter des pieds de groupes

- LiveBindings de A à … : TPrototypeBindSource

Sommaire

- I. Apparence prédéterminée

- II. Apparence dynamique

- II-A. Mise en place

- II-B. Solution

- III. Pour finir

I - Apparence prédéterminée

Avant de passer à l’apparence dynamique, passons d’abord par une apparence prédéterminée pour voir comment cela se réalise.

En préambule, il me faut peut-être éclaircir un premier point. Il est très facile de créer une liste qui affiche des images à condition d’utiliser la bonne apparence d’élément de liste, et ce, sans une once de code. Comme exemple, cette simple petite réalisation créée au moment du design le prouve :

Cependant, dans cette première image, il s’agit de l’affichage d’une image contenue dans la source des données. Peut-être moins connue est l’utilisation de la propriété de l’élément de liste nommée ImageIndex ; or c’est cette propriété, associée à une liste d’images, que nous allons utiliser.

Voici la liste des étapes à suivre.

Étape 1. Sur une forme, ajoutez un TImageList

Remplissez-le avec les images désirées, pour les besoins de la démonstration, seules deux suffisent.

Étape 2. Déposez sur la forme un TPrototypeBindSource

Bien évidemment, il nous faut une source de données. Pour les besoins de la démonstration, je vais utiliser un TProtypeBindSource qui contiendra un champ texte et un champ numérique.

Ajoutez-y un champ texte (LoremIpsum) et un champ numérique entier (UIntegers) :

Gardez en mémoire qu’il s’agit d’une génération aléatoire, la valeur du champ numérique sera donc comprise entre 0 et 200.

Étape 3. Déposez un TListView

Modifiez l’apparence (ItemAppearance/ItemAppearance) de façon à ce que l’élément de liste contienne une image. Vous avez le choix entre plusieurs possibilités :

- ImageListItem ;

- ImageListItemBottomDetail ;

- ImageListItemBottomDetailRightButton ;

- ImageListItemRightButton.

Pour ce premier essai, choisissez ImageListItemBottomDetail.

Reliez le composant TImageList au TListView grâce à la propriété ImageList de ce composant.

Étape 4. Liaison des éléments

Passons maintenant à la partie liaison (LiveBindings) entre la liste et les données.

Pour mémoire, il y a plusieurs possibilités pour appeler le concepteur visuel de liaisons :

En utilisant le menu contextuel du composant (clic droit sur le composant)

En utilisant le menu situé en bas de l’inspecteur d’objet

En utilisant le menu de l’EDI

Voir

Fenêtre d’outils

Concepteur LiveBindings

Toutes les versions de Delphi ne proposent pas ce concepteur, c’est néanmoins le cas à partir de la version Tokyo 10.2*.

* hormis sa version Starter

Par glisser-déposer vous allez alors :

1. Lier la source de données (Synch ↔ *) ;

2. Lier le champ texte (LoremIpsum1 ↔ Item.Text) ;

3. Lier le champ numérique (UIntField1 ↔ Item.Detail) * optionnel ;

4. Lier le champ numérique (UIntField1 ↔ Item.ImageIndex).

Étape 5. Utilisez CustomFormat

Bien évidemment, comme il n’y a pas 200 images dans notre TImageList, il y a peu de chances que des icônes soient affichées. Pour pallier cela, j’ai décidé que toute valeur inférieure ou égale à 60 afficherait l’icône 0 de ma liste et que sinon ce serait l’icône 1.

Dans le panneau de structure, sélectionnez l’expression ImageIndex et indiquez dans la propriété CustomFormat la formule IfThen(value<=60,0,1) :

Et voilà, pas besoin d’exécuter le programme, le résultat est déjà visible en mode design !

II - Apparence dynamique

Si les apparences prédéterminées couvrent la plupart des besoins, nous sommes restreints à n’afficher que quelques éléments. Depuis Delphi XE7, il est possible de personnaliser l’apparence de vue d’une liste (cf. docwiki), ce qui permet déjà de modifier les apparences prédéterminées, mais surtout, ce qui introduit le mode dynamique.

II-A - Mise en place

Repartons de la forme déjà créée au chapitre I, contenant déjà un TImageList.

Étape 1. Ajoutez un nouveau TListView

Cette fois, sélectionnez une apparence dynamique pour vos éléments de liste (DynamicAppearance) :

N’oubliez pas de renseigner la propriété Images du TListView

Étape 2. Ajoutez des objets

Sélectionnez la propriété Item de ItemAppearance dans la vue structure. Puis, dans l’inspecteur d’objets, cliquez sur la propriété + et ajoutez successivement un objet texte (TTextObjectAppearance) et un objet image (TImageObjectAppearance).

Voir, par exemple, cet article de Stephen Ball qui montre le processus à suivre.

Étape 3. Basculez en mode conception

Deux solutions pour le faire :

- soit par le menu situé en bas de l’inspecteur d’objet ;

- soit en utilisant le menu contextuel du composant.

N’hésitez pas à changer le nom de l’objet ajouté (propriété AppearanceObjectName)

Étape 3’. Arrangez la présentation comme bon vous semble

Étape légèrement plus délicate, ce concepteur étant un peu plus difficile à amadouer.

Quelques conseils :

- prenez bien soin de sélectionner l’élément à modifier dans le panneau structure ;

- jJouez sur la propriété Align avant de tenter de modifier les positions (PlaceOffset) de l’objet.

Étape 4. Liaison des éléments

Pour plus de facilité et avoir une sorte de WYSIWYG n’oubliez pas de désactiver le mode conception.

Passez sur le concepteur de liaisons (Livebindings) :

1. Liez la source de données (Synch ↔ *) ;

2. Liez le champ texte (LoremIpsum1 ↔ Item.Text1) ;

3. Liez le champ numérique (UIntField1 ↔ Item.Detail) * optionnel.

Que constate-t-on ? Premier « piège » : Item.Text, bien que disponible, ne sera pas affiché. Il faut utiliser Item.Text1. Mais le problème principal, déclencheur de ce tutoriel, c’est qu’il n’y a pas l’élément Item.ImageIndex, celui qui, fort opportunément, nous était proposé chapitre I, lors de la quatrième étape.

II-B - Solution

Si, lors du chapitre I, aucun code n’était nécessaire, c’est bien en codant que nous arriverons à résoudre l’objectif. Quel événement utiliser ? OnUpdateObjects du TListView sera le candidat idéal.

Pourquoi OnUpdateObjects et non OnUpdatingObjects ? Parce que je vais utiliser un objet de l’élément de liste et non la source des données pour obtenir la valeur. Je préfère donc attendre que tous les objets soient renseignés avant.

Que nous faut-il ? Accéder à l’objet nommé Image mais aussi à la valeur liée à l’objet Detail.

L’accès à ces deux objets peut se faire en utilisant la fonction FindObject (considérée obsolète et remplacée par FindDrawable) ou FindObjectT<T> (<T> indiquant le type d’objet à obtenir).

À partir du moment où ces deux éléments sont trouvés, il suffira de coder ce qu’a fait l’expression du CustomFormat du chapitre I.

procedure TFormDemo.ListViewDynamicUpdateObjects(const Sender: TObject;
const AItem: TListViewItem);
var AListItemBitmap : TListItemImage;
AListItemText : TListItemText;
i : Integer;
begin
AListItemBitmap:=AItem.Objects.FindObjectT<TListItemImage>('Image');
AListItemText:=AItem.Objects.FindObjectT<TListItemText>('Detail');
if Assigned(AListItemBitmap) AND Assigned(AListItemText) then
begin
i:=StrToIntDef(AListItemText.Text,-1);
if i<=60 then  AListItemBitmap.ImageIndex:=0
else  AListItemBitmap.ImageIndex:=1;
end;
end;

Bien évidemment, puisqu’il y a du code, le résultat ne sera visible qu’à l’exécution.

III - Pour finir

Après avoir révisé ou découvert l’utilisation d’une liste d’images en association à un TListView, nous avons découvert que l’apparence dynamique d’un élément de liste n’offrait pas cette possibilité à moins de passer par un peu de code.

Je tiens à remercier l’équipe rédactionnelle Malick pour ses relances, Alain Weber et Alcatîz pour la relecture technique et escartefigue pour les corrections grammaticales et orthographiques.