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.
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.
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.
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.
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 ;
3. Précisez les propriétés Control (ListView1) et DataSource (PrototypeBindSource1) ;
4. Indiquez ensuite les expressions de remplissage (FillExpressions).
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.
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.
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 - |
Ajoutez un TImageObjectAppearance.
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é.
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.
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.
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 ».
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.
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.
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.
Pour obtenir une valeur de couleur en fonction du nom de celle-ci, nous serons obligés d'utiliser l'unité System.UIConsts.
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.
Et voilà le résultat attendu !
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).
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).
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.
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.
{.$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.
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.