FMX : Mettre de la couleur dans un TListView

Utilisation de l'apparence dynamique

Mettre en exergue, via une couleur de fond, l'élément d'un TListView est une question fréquemment posée. Les solutions proposées ne sont pas toujours simples à mettre en œuvre, car, contrairement à une liste simple (TListBox), nous ne pouvons pas passer par des styles personnalisés pour le faire. L'apparition de l'apparence dynamique d'un élément de liste, lors de la sortie de la version Berlin 10.1, va nous permettre de le faire très simplement.

2 commentaires Donner une note à l'article (5)

Article lu   fois.

L'auteur

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

I. Création de l'interface

Pour les besoins de la démonstration, nous avons besoin d'une application multi périphérique dans laquelle nous poserons notre liste (TListView). Comme source de données, nous utiliserons un prototype (TPrototypeBindSource) qui contiendra deux champs : un pour le nom des couleurs, l'autre pour contenir un bitmap.

Image non disponible

Une fois ces deux éléments posés, il nous faudra créer les champs et les lier à la liste pour obtenir une liste aléatoire de couleurs.

I-A. Ajouter les champs au PrototypeBindSource

Trois possibilités s'offrent à nous : l'utilisation du menu en bas de l'inspecteur d'objets, un clic droit sur le composant pour obtenir son menu contextuel, ou plus simplement un double clic sur ce dernier.

Image non disponible

Il nous faut choisir ensuite les types de champs souhaités ; dans notre cas, pour les noms de couleurs, nous utiliserons le générateur ColorsNames et pour l'image, le générateur Bitmaps.

Image non disponible

La propriété RecordCount est égale à -1 c'est-à-dire non limitée, ou plus exactement dans la limite du raisonnable. Nous pouvons limiter le nombre d'éléments à notre besoin (ici 30), histoire de réduire la durée de chargement du programme.

I-B. Marche à suivre pour lier le champ à la liste

Pour ceux qui ont une version professionnelle ou plus de Delphi Berlin, l'utilisation du concepteur visuel de lien rend la chose aisée. Il suffit d'une liaison entre le champ de notre source (ColorsName1) et l'objet de l'item de liste Text pour remplir une liste de base.

Image non disponible

Toutefois, pour l'instant, nous n'avons aucun endroit pour lier l'image à l'item !

I-B-1. Version Starter

Pour ceux qui ont une version sans ce concepteur, voici les étapes à suivre :

1. Posez un composant TBindingsList. Comme pour le TPrototypeBindSource, un clic droit, un double clic, ou les options du menu en bas de l'inspecteur d'objets, vous permettent ensuite d'ajouter les liens ;

2. Ajoutez un lien de type LinkListControlToField ;

Image non disponible

3. Précisez les propriétés Control (ListView1) et DataSource (PrototypeBindSource1) ;

Image non disponible

4. Indiquez ensuite les expressions de remplissage (FillExpressions).

Image non disponible

Ajoutez un membre à ces expressions avec les propriétés :

Propriété

Valeur

Explications

ControlMembreName

Text

Correspond à l'objet de l'élément de la liste que nous voulons utiliser.

CustomFormat

   

SourceMemberName

ColorsName1

Le nom du champ de notre TPrototypeBindSource.

Vous obtiendrez alors le même résultat que précédemment, avec une version plus performante de l'EDI.

I-C. Modifier la présentation de l'item de liste

Par défaut, à la création de la liste, l'apparence de l'élément est de type ListItem, et ListItemShowCheck si cette liste est passée en mode modification.

Pour avoir une image que nous pourrons manipuler, nous allons modifier ce type en DynamicAppearance. Le nom de la couleur a disparu ! J'y reviendrai plus un peu plus loin.

La fausse bonne idée serait d'utiliser le type ImageListItem qui, s'il semble à première vue adéquat, ne nous permettra pas la suite des opérations.

Image non disponible

J'ai surligné sur l'image les points importants à prendre en compte.

Commencez par basculer en mode conception de l'apparence. Vous avez le choix entre utiliser l'option de menu en bas de l'inspecteur d'objets ou faire un simple clic droit sur le ListView.

Image non disponible

Se positionner sur l'item nous permet alors d'ajouter certains autres éléments :

TTextObjectAppearance

Un texte

TImageObjectAppearance

Une image

TAccessoryObjectAppearance

Un bouton accessoire (en général, la flèche vers la droite)

TButtonObjectAppearance

Un bouton permettant de mettre un texte à l'intérieur

TGlyphButtonObjectAppearance

Une case à cocher, ou un bouton + ou -
Attention, uniquement pour le mode édition, donc pour la collection d'objets d'ItemEditObjects.

Ajoutez un TImageObjectAppearance.

Image non disponible

Comme nous voulons que l'image ait la largeur de la liste, nous passerons la propriété ScalingMode à Stretch.

I-D. Modifier les liaisons

La liaison avec les données, sans être supprimée, ne fonctionne plus ! Pourquoi ? Tout simplement parce que nous n'affichons plus les mêmes objets. Lors de la liaison précédente, le texte était lié à Text, mais comme il s'agit d'une apparence dynamique, cette propriété n'est pas utilisée. Il faut modifier la liaison pour pointer sur l'objet créé par défaut au moment du changement de type d'apparence : Text1.

Ceux qui ont la possibilité d'avoir le concepteur visuel de lien verront aisément la différence. Vous remarquerez que vous pouvez alors facilement lier le champ Bitmap1 à l'Item.Image2 ajouté.

Image non disponible

I-D-1. Version Starter

Pour les détenteurs d'une version Starter, il faudra repasser sur le TBindingsList et retourner sur les expressions de remplissage (comme à l'étape 4) pour modifier la propriété ControlMembreName.

Image non disponible

Ajoutez un second élément de liaison entre Image2, pour ce qui est de la propriété ControlMembreName, et Bitmap1 pour la valeur de SourceMemberName.

I-D-2. Résultat

à ce stade une couleur apparaît dans la liste, mais le nom de la couleur n'apparaît toujours pas !

La génération du jeu d'essai étant aléatoire, le rendu pourra vous surprendre : il suffit de fermer et de rouvrir l'application pour avoir des résultats différents.

Image non disponible

Cette absence de nom s'explique tout bêtement par le fait que l'image est au premier plan.

I-E. L'astuce

Visualisez le texte de la fiche : clic droit sur la fiche et option « Voir comme texte ».

Avant
Sélectionnez
  object ListView1: TListView
    ItemAppearanceClassName = 'TDynamicAppearance'
    ItemEditAppearanceClassName = 'TDynamicAppearance'
    HeaderAppearanceClassName = 'TListHeaderObjects'
    FooterAppearanceClassName = 'TListHeaderObjects'
    ItemIndex = 0
    Align = Client
    Size.Width = 278.000000000000000000
    Size.Height = 460.000000000000000000
    Size.PlatformDefault = False
    TabOrder = 0
    ItemAppearanceObjects.ItemObjects.ObjectsCollection = <
      item
        AppearanceObjectName = 'Text1'
        AppearanceClassName = 'TTextObjectAppearance'
      end
       item
        AppearanceObjectName = 'Image2'
        AppearanceClassName = 'TImageObjectAppearance'
        Appearance.ScalingMode = Stretch
         end>
    ItemAppearanceObjects.ItemEditObjects.ObjectsCollection = <
      item
        AppearanceObjectName = 'Text1'
        AppearanceClassName = 'TTextObjectAppearance'
      end>
  end

Déplacez les éléments de façon à mettre l'item image avant l'item texte.

Après
Sélectionnez
  object ListView1: TListView
    ItemAppearanceClassName = 'TDynamicAppearance'
    ItemEditAppearanceClassName = 'TDynamicAppearance'
    HeaderAppearanceClassName = 'TListHeaderObjects'
    FooterAppearanceClassName = 'TListHeaderObjects'
    OnUpdatingObjects = ListView1UpdatingObjects
    ItemIndex = 0
    ItemSpaces.Left = 0.000000000000000000
    ItemSpaces.Right = 0.000000000000000000
    Align = Client
    Size.Width = 278.000000000000000000
    Size.Height = 460.000000000000000000
    Size.PlatformDefault = False
    TabOrder = 0
    ItemAppearanceObjects.ItemObjects.ObjectsCollection = <
      item
        AppearanceObjectName = 'Image2'
        AppearanceClassName = 'TImageObjectAppearance'
        Appearance.ScalingMode = Stretch
      end
      item
        AppearanceObjectName = 'Text1'
        AppearanceClassName = 'TTextObjectAppearance'
      end>
    ItemAppearanceObjects.ItemEditObjects.ObjectsCollection = <
      item
        AppearanceObjectName = 'Text1'
        AppearanceClassName = 'TTextObjectAppearance'
      end>
  end

Puis rebasculez en mode fiche : clic droit option « Voir comme fiche » ou Alt+F12. Le mode conception de l'item est désactivé, mais nous voyons bien la liste aléatoire de couleurs.

Image non disponible

I-F. Utiliser l'événement

Il est évident qu'il nous reste une dernière étape à accomplir, car l'image ne correspond absolument pas, sauf hasard heureux, à l'intitulé. Pour remplir notre image, il faut programmer l'événement de liste OnUpdatingObjects.

Image non disponible

Pour obtenir une valeur de couleur en fonction du nom de celle-ci, nous serons obligés d'utiliser l'unité System.UIConsts.

Programme
Sélectionnez
unit Unit1;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  System.UIConsts,  // ajout de l'unité pour obtenir la valeur de la couleur
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
  FMX.ListView.Types, FMX.ListView.Appearances, FMX.ListView.Adapters.Base,
  Data.Bind.GenData, Data.Bind.EngExt, Fmx.Bind.DBEngExt, System.Rtti,
  System.Bindings.Outputs, Fmx.Bind.Editors, Data.Bind.Components,
  Data.Bind.ObjectScope, FMX.ListView;

type
  TForm1 = class(TForm)
    ListView1: TListView;
    PrototypeBindSource1: TPrototypeBindSource;
    BindingsList1: TBindingsList;
    LinkListControlToField1: TLinkListControlToField;
    procedure ListView1UpdatingObjects(const Sender: TObject;
      const AItem: TListViewItem; var AHandled: Boolean);
  private
    { Déclarations privées }
  public
    { Déclarations publiques }
  end;

var
  Form1: TForm1;

implementation

{$R *.fmx}

procedure TForm1.ListView1UpdatingObjects(const Sender: TObject;
  const AItem: TListViewItem; var AHandled: Boolean);
var AListItemBitmap : TListItemImage;
    AListItemText : TListItemText;
    AColor : TAlphaColor;
begin
AListItemBitmap:=AItem.Objects.FindObjectT<TListItemImage>('Image2');
AListItemText:=AItem.Objects.FindObjectT<TListItemText>('Text1');

if assigned(AListItemBitmap) then
 begin
    AListItemBitmap.Bitmap:=TBitmap.Create(40,40);
    try
       AColor:=StringToAlphaColor(AListItemText.Text)
    except // certaines couleurs sont inconnues! i.e. monneygreen, ltgrey
      AColor:=TAlphaColorRec.Null;
    end;
    AListItemBitmap.Bitmap.Clear(AColor);
 end;
end;

end.

I-F-1. Quelques explications s'imposent

AListItemBitmap : il nous faut récupérer l'adresse de l'image dans la collection ; ce sera chose faite via FindObjectT.

AListItemText : idem ; cela nous permettra de récupérer le texte qui nous servira à obtenir la valeur de la couleur.

Enfin, étonnamment, certains noms de couleurs générées sont inconnus dans l'unité System.UIConsts (en particulier monneygreen et ltgrey) ; il m'a donc fallu mettre la conversion à l'intérieur d'un bloc d'exception. Échaudé, j'en ai profité pour vérifier l'existence de mon image, d'où le if assigned(AListItemBitMap).

Dernier petit point pour obtenir un bon rendu : vérifier ItemsSpaces. Les marges gauche et droite sont respectivement de 10 et 11 par défaut. Réglez-les en fonction de vos besoins.

Image non disponible

Et voilà le résultat attendu !

Image non disponible

II. Un peu plus loin

Le principe des couleurs de fond est posé. Mais qu'en est-il si l'on voulait faire de même à partir de données ? Pour la démonstration, nous avons juste besoin d'ajouter à notre source de données un entier (il faut alors choisir le générateur de type Integer).

Image non disponible

Seconde astuce de ce tutoriel : comment accéder à la valeur de cet entier ? À partir d'un PrototypeBindSource c'est loin d'être évident, aussi suis-je tout simplement passé par un lien entre ce champ et le Text de l'élément (je vous rappelle que celui-ci n'apparaît pas à cause de l'apparence dynamique).

Image non disponible

II-A. Version Starter

Vous devrez passer par TBindingsList pour ajouter la liaison. Sélectionnez ensuite le ListLinkContolToField1 pour accéder aux propriétés FillExpressions. Cliquez alors sur les pour accéder aux expressions.

Image non disponible

Ajoutez alors la liaison entre Text et IntField1.

II-B. Code

Nous pouvons maintenant utiliser cette valeur selon notre besoin. Ici, j'ai décidé de tester le modulo 3 de cet entier pour manipuler trois couleurs.

Comme l'entier généré peut être négatif, il faut utiliser sa valeur absolue.

Step2
Sélectionnez
{.$DEFINE STEP2}

procedure TForm1.ListView1UpdatingObjects(const Sender: TObject;
  const AItem: TListViewItem; var AHandled: Boolean);
var AListItemBitmap : TListItemImage;
    AListItemText : TListItemText;
    AColor : TAlphaColor;
    i : word;
begin
AListItemBitmap:=AItem.Objects.FindObjectT<TListItemImage>('Image2');
{$IFNDEF STEP2}
AListItemText:=AItem.Objects.FindObjectT<TListItemText>('Text1');
if Assigned(AListItemBitmap) then
 begin
    AListItemBitmap.Bitmap:=TBitmap.Create(40,40);
    try
       AColor:=StringToAlphaColor(AListItemText.Text)
    except // certaines couleurs sont inconnues! i.e. monneygreen, ltgrey
      AColor:=TAlphaColorRec.Null;
    end;
    AListItemBitmap.Bitmap.Clear(AColor);
 end;
{$ENDIF}
{$IFDEF STEP2}
if Assigned(AListItemBitmap) then
 begin
    i:=Abs(AItem.Text.ToInteger) mod 3;
       case i of
         0 : AColor:=TAlphaColorRec.Green;
         1 : AColor:=TAlphaColorRec.Yellow;
         2 : AColor:=TAlphaColorRec.Red;
       end;
    AListItemBitmap.Bitmap.Clear(AColor);
 end;
{$ENDIF}

end;

Pour pouvoir tester cette option, il suffit de dé-commenter la clause $DEFINE STEP2 du code source.

Image non disponible

Les couleurs piquent-elles les yeux ? Optez pour une version plus light en les changeant, par exemple, en chartreuse, gold et indianred.

III. Conclusion

L'apparence dynamique d'un item de liste permet de coloriser rapidement les éléments en fonction des données, et ce sans passer par la création complexe d'éléments personnalisés via la création d'un package (démarche indiquée dans cet exemple).

Suggestion de lectures : Personnalisation de l'apparence de la vue.

Point de conclusion sans remerciements aux premiers lecteurs : Nabil et Thierry, au bêta-testeur free07 pour la partie OSX et bien sûr aux relecteurs officiels du forum. Que cela soit de la relecture technique patiente de Gilles au correcteur orthographique et grammatical Maxy35. Sans eux, point de publication, donc encore merci à eux !

Vous pourrez télécharger les sources de ce projet à cette adresse Lien de téléchargement.

Toutefois, pour une bonne maîtrise, je vous suggère plutôt de tenter de reproduire l'exemple.

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

En complément sur Developpez.com

  

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.