I. Construction de l'interface▲
Pour cette démonstration, j'ai besoin d'un TListView et d'une source de données quelconque pour remplir celle-ci. Dans une nouvelle application multipériphérique, je pose donc un TListView qui prendra tout l'espace client (align=alclient).
Pour les données, j'utilise un TPrototypeBindSource qui m'évitera d'avoir à utiliser des composants d'accès aux bases de données et surtout de créer une table et de fournir un jeu d'essai. Le TPrototypeBindSource générera des données aléatoires en fonction du type demandé ; il faut juste faire attention à ce que la liste de données soit ordonnée.
J'ajoute ensuite deux colonnes à mon TPrototypeBindSource en utilisant son éditeur de champs que l'on obtient soit en double-cliquant sur le composant, soit en utilisant son menu contextuel.
Notez bien que la propriété Options.optShuffle est à False. C'est ce qui permettra d'obtenir une liste de noms de couleurs en ordre alphabétique.
La deuxième colonne sera un entier. C'est cette valeur que je cumulerai par la suite pour chaque groupe.
J'améliore alors l'affichage au sein de ma liste en modifiant tout d'abord l'apparence de mes éléments en adaptant la propriété ItemApperance.ItemApperance du TListView en DynamicAppearance.
La modification en apparence dynamique me permet ensuite de basculer la liste en mode conception.
Pour basculer en mode conception, deux possibilités : soit utiliser le menu contextuel du composant TListView, soit utiliser le menu situé sous la liste des propriétés (voir image ci-dessous).
J'obtiens ainsi un espace de conception sur mon composant que je vais pouvoir arranger selon mes besoins.
Tout d'abord, je réduis la hauteur de mon texte principal qui contiendra par la suite le nom de la couleur.
Puis je vais ajouter une seconde zone pour l'entier associé.
J'ajoute donc un TTextObjectAppearance supplémentaire.
Il me faudra également retravailler la taille et la position de cette zone.
Cette étape est assez délicate. Ce concepteur est certainement une des fonctionnalités à améliorer.
À la fin de la conception, après quelques améliorations des tailles et des couleurs des éléments, j'obtiens l'affichage ci-dessous.
Il est temps à présent de rebasculer la liste en mode normal et de passer à la partie liaison de celle-ci avec les données.
II. Liaison entre la liste et les données▲
Une fois de plus, les possesseurs d'une version professionnelle ou plus ont l'avantage du concepteur visuel de liaisons ! Je rassure les possesseurs de versions inférieures : si je commence par le concepteur visuel, je décrirai ensuite comment faire sans.
II-A. Avec le concepteur visuel de liaisons▲
Je dois en premier lieu lier la source de données, représentée par l'astérisque du PrototypeBindSource1 à la liste de façon synchrone (Synch).
Puis je lie mes deux colonnes. La « magie » opère déjà : ma liste est remplie, sans avoir écrit une seule ligne de commande !
Je vais d'abord me concentrer sur les entêtes. À ce stade, la première idée serait de continuer avec le concepteur de liaisons et de lier NomCouleurs aux deux propriétés citées afin d'obtenir le schéma suivant. Cependant, il me faudra par la suite aller modifier ces deux liens.
À mon avis, il est moins fastidieux de modifier directement les propriétés.
Une fois de plus la « magie » opère : aucune ligne de code et pourtant ma liste est bien groupée !
Aucune ligne de code, c'est vite écrit ! Si effectivement il n'y a toujours aucune ligne de code dans le source du programme, j'ai quand même utilisé une méthode du moteur des LiveBindings (SubString), à la fois pour indiquer comment grouper (propriété FillBreakCustomFormat) et comment afficher le texte de l'entête de groupe (propriété FillHeaderCustomFormat).
II-B. Sans le concepteur visuel de liaisons▲
Les images écran qui vont suivre ont été réalisées avec une version Starter de Tokyo 10.2.0.
Première étape indispensable, il faut ajouter un composant BindingsList à la forme principale.
Un double-clic sur ce nouveau composant permet d'ouvrir la liste des liens qui nous permettra d'en ajouter (plus rapidement, il est possible d'utiliser le menu contextuel du composant et utiliser l'option du menu « Nouveau composant LiveBindings »). Comme type de lien à ajouter, je sélectionne TLinkListControlToField.
Troisième étape, je remplis les propriétés principales du lien, à savoir :
- la propriété Control ;
- la propriété DataSource.
Puis je clique sur la propriété FillExpressions pour faire la liaison entre les données et les différents libellés de mes éléments de liste.
Reste ensuite à procéder de la même manière qu'au chapitre précédent pour gérer les entêtes et j'obtiens le même résultat qu'avec le concepteur visuel de liaisons.
La liste ne se remplit pas, ou, pour reprendre ma formule, la « magie » n'opère pas ! Si c'est le cas, il suffit de rafraîchir l'ensemble des données, c'est-à-dire : basculer la propriété AutoActivate du PrototypeBindSource1 à False puis la remettre à True.
III. Ajouter des pieds de groupes▲
J'ai maintenant une liste groupée de mes données, ce qui amène à l'étape suivante. Le concepteur visuel de liaisons montre qu'il est possible de lier un ItemHeaderText et un ItemHeaderBreak, mais ne dit rien au sujet des Footers. Il est vrai que pour ceux qui n'ont que la version starter, n'ayant pas de visuel, la question est simplement : comment remplir mon pied de groupe ?
La logique voudrait pourtant que, comme pour les entêtes d'éléments, il y ait moyen de faire une liaison, mais ces propriétés n'apparaissent pas !
Faut-il pour autant abandonner les LiveBindings et coder dans le programme le remplissage de la liste ?
III-A. Logique▲
Tout d'abord, un peu de logique pour expliquer comment procéder pour remplir une liste.
III-B. Les événements de TLinkListControlToField▲
Après la logique, il faut procéder à un état des lieux : quels sont les moyens dont je dispose pour gérer le remplissage de la liste via la liaison ? C'est ici qu'entrent en scène les événements de la liaison. Toutefois, l'aide sur ces derniers n'est pas très explicite. Pour tous ces événements, il n'existe qu'une seule phrase que je cite :
« Événement se produisant quand les expressions de liaison de ce wrapper délégué ont été activées. »
J'ai tenté, dans le tableau qui suit, de mettre en relation les événements de ce lien avec ceux d'un ensemble de données :
OnActivated |
AfterOpen |
OnActivating |
BeforeOpen |
OnAssignedValue |
? |
OnAssigningValue |
AfterGetRecord |
OnEvalError |
OnError (se passe de commentaire) |
OnFilledList |
OnClose ou plus exactement quand la fin du fichier est atteinte |
OnFilledListItem |
AfterScroll ou AfterRowRequest ? |
OnFillingList |
? |
OnFillingListItem |
BeforeScroll ou AfterRowRequest ? |
Ce tableau n'est qu'une tentative personnelle de compréhension du mécanisme de remplissage de liste.
III-C. Programmation ▲
Pour ce qui est de la détection du remplissage d'un élément de la liste, l'événement évident est OnFilledListItem.
Restait à savoir :
- Comment accéder aux valeurs ?
- Comment savoir de quel type d'élément il s'agit ?
À ces deux questions, l'événement propose un paramètre AEditor de type interface d'un IbindListEditorItem. Il est donc possible d'obtenir un TListViewItem en transtypant l'objet de cet « éditeur ».
procedure
TForm13.LinkListControlToField1FilledListItem(Sender: TObject;
const
AEditor: IBindListEditorItem);
var
AnItem : TListViewItem;
begin
AnItem:=AEditor.CurrentObject as
TListViewItem;
Obtenir le type d'élément ajouté est alors possible en testant l'objectif de l'élément avec la propriété Purpose.
Trois valeurs sont possibles :
- None pour un élément normal ;
- Header pour un entête de groupe ;
- Footer pour un pied de groupe.
Je peux donc coder une partie de mon logigramme ainsi.
procedure
TForm13.LinkListControlToField1FilledListItem(Sender: TObject;
const
AEditor: IBindListEditorItem);
// le remplissage d'un item est fini
var
AnItem : TListViewItem;
begin
AnItem:=AEditor.CurrentObject as
TListViewItem;
if
(AnItem.Purpose=TListItemPurpose.Header) then
begin
// Ajout du Pied de Groupe si ce n'est pas le premier groupe
if
(AnItem.Index
>1
) then
begin
with
ListView1.Items.Add do
begin
Text := Format('Total %s %d'
,[idBloc,Cumul]);
Purpose := TListItemPurpose.Footer;
end
;
end
;
idBloc:=AnItem.Text;
Cumul:=0
;
end
;
À ce stade, une exécution du programme fournit le résultat suivant :
Constat : le pied de groupe se trouve sous l'entête du groupe prochain et non au-dessus. La raison ? J'ai utilisé l'ajout d'un nouvel élément de liste : indiqué le type d'élément ne change rien. Il aurait donc fallu, plutôt que faire un ajout, faire une insertion. Toutefois, mes tentatives en ce sens ont échoué, les pieds se retrouvant tous ensemble et la plupart des éléments de ma liste vides !
Donc, il me faut déplacer ces pieds de groupes. Le mieux est de le faire une fois la liste remplie. Or, cela tombe bien puisqu'il me reste un dernier pied à ajouter, celui de fin de fichier.
J'utilise donc l'événement OnFilledList pour satisfaire cette dernière exigence.
procedure
TForm13.LinkListControlToField1FilledList(Sender: TObject);
// le remplissage de la liste est fini
var
Pied : TListViewItem;
texte : String
;
Li : integer
;
begin
// Déplacement des Pieds d'une ligne vers le haut
for
Li := 1
to
Pred(ListView1.ItemCount) do
begin
if
ListView1.Items[Li].Purpose=TListItemPurpose.Footer then
begin
// récupére le texte du pied
Pied:=ListView1.Items[Li];
Texte:=Pied.Text;
// Supprime le pied créé (pas d'autre solution ?)
ListView1.Items.Delete(Li);
// Insére le nouveau pied
with
ListView1.Items.Insert(Li-1
) do
begin
Text := Texte;
Purpose := TListItemPurpose.Footer;
end
;
end
;
end
;
// Ajout de la dernière totalisation (=fin de fichier)
with
ListView1.Items.Add do
begin
Text := Format('Total %s %d'
,[IdBloc,Cumul]);
Purpose := TListItemPurpose.Footer;
end
;
end
;
Une nouvelle exécution du code montre la liste groupée enfin conforme aux attentes.
III-C-1. Pistes à explorer, questions sans réponses ▲
Confronté à la problématique, j'ai peut-être pris un chemin peu académique. En tout cas, je trouve que l'opération consistant à remonter le pied de groupe à la fin du remplissage de la liste, si elle est efficace, n'est pas très satisfaisante. Y aurait-il un moyen d'utiliser l'événement OnFillingListItem ?
Avec cet exemple, je n'ai pas vérifié le comportement des cumuls en cas de mise à jour d'un des éléments.
IV. Application▲
Il est temps de faire un bond dans une vie plus réelle. Pour montrer comment il est possible d'utiliser ce qui a été expliqué précédemment, je vais utiliser des fichiers exemples fournis avec Delphi dans le répertoire C:\Users\Public\Documents\Embarcadero\Studio\19.0\Samples\Data . En l'occurrence, je vais utiliser les fichiers de commandes (orders.cds) et clients (customers.cds) proposés et faire une liste groupée des commandes par client.
Il y a peu de changements par rapport au programme précédent si ce n'est l'utilisation de TClientDatasets au lieu des données aléatoires d'un TPrototypeBindSource.
L'objectif est d'indiquer le nom du client (Company) en entête et le montant total des ordres en pied de groupe. En détail, j'indiquerai la date de l'ordre (SaleDate), son montant total (ItemsTotal) et le montant payé (AmountPaid) fera office de cerise sur le gâteau en me permettant, lors d'une étape de finalisation, de faire ressortir en couleur les impayés.
IV-A. Préparer les données▲
Au risque de me répéter, l'important est avant tout l'ordre de l'ensemble de données qui va remplir la liste. Pour cela, je vais indiquer la liste des colonnes qui me serviront d'index pour mon TClientDataset.
Je déclare ensuite toutes les colonnes nécessaires (double-clic sur le composant pour obtenir la fenêtre de l'éditeur de champs) et sélectionne les colonnes qui vont me servir.
Ensuite, j'ajoute une nouvelle colonne de type recherche de façon à obtenir le nom de la société (Company) en fonction de l'identifiant du client (Custno).
IV-B. Préparer la liste▲
J'applique la méthode exposée au chapitre I Construction de l'interface, c'est-à-dire l'utilisation d'une apparence dynamique pour mes éléments de liste puis je bascule en mode conception pour ajouter et retailler les différents objets textes ajoutés (cf. image en début de chapitre)
IV-C. Remplissage de la liste▲
Le remplissage de la liste va se faire en deux temps : tout d'abord l'utilisation des liaisons (Livebindings) puis un peu de codification des événements de la liaison.
IV-C-1. Avec le concepteur de liaisons visuel▲
Via le concepteur, le remplissage est presque un jeu d'enfant. Il faut obtenir le schéma suivant :
IV-C-2. Sans le concepteur visuel▲
Sans le concepteur visuel, il faut suivre les étapes du chapitre II.B Sans le concepteur visuel de liaisons
À la fin de l'étape de liaison, j'obtiens déjà un premier résultat.
IV-C-3. Codification▲
Comme exposé au chapitre III.C Programmation, la suite passe par la codification des deux événements OnFilledListItem et OnFilledList de la liaison LinkListControlToFielField.
procedure
TForm15.LinkListControlToField1FilledList(Sender: TObject);
var
Pied : TListViewItem;
texte : String
;
Li : integer
;
begin
// Déplacement des Pieds d'une ligne vers le haut
for
Li := 1
to
Pred(ListView1.ItemCount) do
begin
if
ListView1.Items[Li].Purpose=TListItemPurpose.Footer then
begin
// récupére le texte du pied
Pied:=ListView1.Items[Li];
Texte:=Pied.Text;
// Supprime le pied créé (pas d'autre solution ?)
ListView1.Items.Delete(Li);
// Insère le nouveau pied
with
ListView1.Items.Insert(Li-1
) do
begin
Text := Texte;
Purpose := TListItemPurpose.Footer;
end
;
end
;
end
;
// Ajout de la dernière totalisation (=fin de fichier)
with
ListView1.Items.Add do
begin
Text := Format('Total %s %3.2f'
,[idCust,Cumul]);
Purpose := TListItemPurpose.Footer;
end
;
end
;
procedure
TForm15.LinkListControlToField1FilledListItem(Sender: TObject;
const
AEditor: IBindListEditorItem);
// le remplissage d'un item est fini
var
AnItem : TListViewItem;
begin
AnItem:=AEditor.CurrentObject as
TListViewItem;
if
(AnItem.Purpose=TListItemPurpose.Header) then
begin
// Ajout du Pied de Groupe si ce n'est pas le premier groupe
if
(AnItem.Index
>1
) then
begin
with
ListView1.Items.Add do
begin
Text := Format('Total %s %3.2f'
,[idCust,Cumul]);
Purpose := TListItemPurpose.Footer;
end
;
// cet item devra être déplacé vers le haut
end
;
idCust:=OrdersSociete.asString;
Cumul:=0
;
end
else
begin
// Cumul par client
Cumul:=Cumul+Orders.FieldByName('ItemsTotal'
).AsCurrency;
end
;
end
;
À l'exécution, les pieds de groupes sont bien calculés et affichés.
Pour donner un peu plus de corps à la liste, j'ai ajouté un style (TStyleBook) chargé à partir du fichier MetropolisUIBlue.Style
IV-D. Finalisation▲
Dans le fichier proposé, nous remarquons que certaines factures ne sont pas encore payées (AmountPaid=0) : tant qu'à faire, il serait bon de pouvoir mettre celles-ci en exergue.
Pour faire ceci, en guise de première étape, il faut ajouter un TImageObjectAppearance.
Je le concède, en mode conception, c'est loin d'être séduisant ! En rebasculant en mode normal, l'image est invisible. Pour revoir tous mes objets textes, il me faudra modifier le code source du fichier dfm afin de mettre l'élément image en début de collection.
object
Form15: TForm15
Left = 0
Top = 0
Caption = 'Form15'
ClientHeight = 526
ClientWidth = 529
StyleBook = StyleBook1
FormFactor.Width = 320
FormFactor.Height = 480
FormFactor.Devices = [Desktop]
DesignerMasterStyle = 0
object
ListView1: TListView
ItemAppearanceClassName = 'TDynamicAppearance'
ItemEditAppearanceClassName = 'TDynamicAppearance'
HeaderAppearanceClassName = 'TListHeaderObjects'
FooterAppearanceClassName = 'TListHeaderObjects'
AlternatingColors = True
ItemIndex = 1
ItemSpaces.Right = 0
.000000000000000000
Align = Left
Size.Width = 385
.000000000000000000
Size.Height = 526
.000000000000000000
Size.PlatformDefault = False
TabOrder = 4
ItemAppearance.HeaderHeight = 30
ItemAppearance.FooterHeight = 30
ItemAppearanceObjects.ItemObjects.ObjectsCollection = <
item
AppearanceObjectName = 'Image4'
AppearanceClassName = 'TImageObjectAppearance'
Appearance.ScalingMode = Stretch
Appearance.Opacity = 0
.500000000000000000
end
item
AppearanceObjectName = 'Text1'
AppearanceClassName = 'TTextObjectAppearance'
Appearance.TextAlign = Leading
Appearance.Width = 111
.000000000000000000
Appearance.Height = 25
.000000000000000000
end
item
AppearanceObjectName = 'Text2'
AppearanceClassName = 'TTextObjectAppearance'
Appearance.TextAlign = Trailing
Appearance.Width = 158
.000000000000000000
Appearance.Height = 25
.000000000000000000
Appearance.PlaceOffset.X = 198
.000000000000000000
end
item
AppearanceObjectName = 'Text3'
AppearanceClassName = 'TTextObjectAppearance'
Appearance.TextAlign = Trailing
Appearance.Width = 158
.000000000000000000
Appearance.Height = 21
.000000000000000000
Appearance.PlaceOffset.X = 198
.000000000000000000
Appearance.PlaceOffset.Y = 24
.000000000000000000
end
Il faut à présent coder de façon à remplir cette image. Dans le tutoriel FMX : Mettre de la couleur dans un TListView, j'ai proposé l'utilisation de l'événement OnUpdatingObject du composant TListView. Fort de mes nouvelles expériences, je propose plutôt de faire l'opération à l'intérieur de l'événement OnFilledListItem déjà codé.
procedure
TForm15.LinkListControlToField1FilledListItem(Sender: TObject;
const
AEditor: IBindListEditorItem);
// le remplissage d'un item est fini
var
AnItem : TListViewItem;
Montant : String
;
AListItemBitmap : TListItemImage;
AColor : TAlphaColor;
begin
AnItem:=AEditor.CurrentObject as
TListViewItem;
if
(AnItem.Purpose=TListItemPurpose.Header) then
begin
// Ajout du Pied de Groupe si ce n'est pas le premier groupe
if
(AnItem.Index
>1
) then
begin
with
ListView1.Items.Add do
begin
Text := Format('Total %s %3.2f'
,[idCust,Cumul]);
Purpose := TListItemPurpose.Footer;
end
;
// cet item devra être déplacé vers le haut
end
;
idCust:=OrdersSociete.asString;
Cumul:=0
;
end
else
begin
// Gestion de la couleur ---------------------------------------------
AListItemBitmap:=AnItem.Objects.FindObjectT<TListItemImage>('Image4'
);
if
assigned(AListItemBitmap) then
begin
AListItemBitmap.Bitmap:=TBitmap.Create(40
,40
);
if
OrdersAmountPaid.AsCurrency<OrdersItemsTotal.asCurrency then
AColor:=TAlphacolorRec.Crimson
else
AColor:=TAlphaColorRec.Null;
AListItemBitmap.Bitmap.Clear(AColor);
end
;
// ----------------------------------------------------------
// Cumul par client
Cumul:=Cumul+Orders.FieldByName('ItemsTotal'
).AsCurrency;
end
;
end
;
À l'exécution, j'obtiens alors ceci :
Cette implémentation m'a même permis de répondre à une question que je me posais : comment ajouter des informations fixes à ma liste ? En effet, l'utilisation de CustomFormat pour les montants n'est pas très satisfaisante, les libellés n'étant pas alignés. La présentation est donc encore perfectible. J'ai par conséquent ajouté deux éléments textes et supprimé ces CustomFormat inélégants.
Un peu de code supplémentaire finalisera ma procédure.
procedure
TForm15.LinkListControlToField1FilledListItem(Sender: TObject;
const
AEditor: IBindListEditorItem);
// le remplissage d'un item est fini
// le remplissage d'un item est fini
var
AnItem : TListViewItem;
AListItemBitmap : TListItemImage;
AListItemTextFixe : TListItemText;
AColor : TAlphaColor;
begin
AnItem:=AEditor.CurrentObject as
TListViewItem;
if
(AnItem.Purpose=TListItemPurpose.Header) then
begin
// Ajout du Pied de Groupe si ce n'est pas le premier groupe
if
(AnItem.Index
>1
) then
begin
with
ListView1.Items.Add do
begin
Text := Format('Total %s %3.2f'
,[Customer,Cumul]);
Purpose := TListItemPurpose.Footer;
end
;
// cet item devra être déplacé vers le haut
end
;
idCust:=OrdersCustNo.Value;
Customer:=OrdersSociete.asString;
Cumul:=0
;
end
else
begin
// Ajout des libellés fixes
AListItemTextFixe:=AnItem.Objects.FindObjectT<TListItemText>('Text6'
);
if
Assigned(AListItemTextFixe) then
AListItemTextFixe.Text:='Montant Total'
;
AListItemTextFixe:=AnItem.Objects.FindObjectT<TListItemText>('Text7'
);
if
Assigned(AListItemTextFixe) then
AListItemTextFixe.Text:='Payé'
;
// Gestion de la couleur ------------------------------------------
AListItemBitmap:=AnItem.Objects.FindObjectT<TListItemImage>('Image4'
);
if
assigned(AListItemBitmap) then
begin
AListItemBitmap.Bitmap:=TBitmap.Create(40
,40
);
if
OrdersAmountPaid.AsCurrency<OrdersItemsTotal.asCurrency
then
AColor:=TAlphacolorRec.Crimson
else
AColor:=TAlphaColorRec.Null;
AListItemBitmap.Bitmap.Clear(AColor);
end
;
// ----------------------------------------------------------
// Cumul par client
Cumul:=Cumul+Orders.FieldByName('ItemsTotal'
).AsCurrency;
end
;
end
;
J'ai même profité de l'occasion pour associer à la liste une boîte de recherche. Toutefois, cette recherche ne fonctionne que sur les éléments de liste et pas sur les parties entêtes ou pieds de groupes, et c'est dommage !
Une astuce pour pouvoir pallier cette lacune ? Comme la recherche ne se fait que sur les éléments de l'item de liste, rien n'empêche d'ajouter un (ou plusieurs) nouveau(x) TTextObjectAppareance, mais de le(s) rendre invisible(s).
Par exemple, j'ai ajouté un élément texte, lié à la colonne Société :
J'ai alors la possibilité de faire la recherche sur le nom du client.
Aucune ligne de code n'a été écrite pour ajouter cette fonctionnalité de recherche !
V. Mettre à bas le jeu des chaises musicales entre entête et pied de groupe▲
Soit vous avez suivi mon cheminement et, comme moi, vous trouvez que la solution proposée n'est pas très orthodoxe, soit vous avez sauté les étapes intermédiaires pour obtenir mes dernières cogitations.
Pour ceux qui accèdent directement à ce chapitre, un seul prérequis : savoir comment grouper des éléments de listes via les LiveBindings et savoir qu'un lien entre une liste et sa source, un TLinkListControlToField, a aussi ses propres événements.
Pour résumer, l'astuce consiste donc à utiliser les événements de ce lien pour, lors de la création d'un élément de type Header, ajouter à la volée un autre élément que l'on définit ensuite de type Footer.
L'élément de type Header est créé automatiquement par le moteur des LiveBindings. Mon raisonnement a été de me poser la question : « ne pourrait-on pas changer ce type sans que cela influe sur le comportement du moteur ? ».
Eh bien, oui ! C'est possible : il suffit alors de remplir le texte de l'élément créé automatiquement du texte de bas de groupe, de changer sa finalité (Purpose) et de faire de l'élément créé à la volée (qui se retrouvera dessous, je le rappelle) non plus le pied de groupe mais l'entête du nouveau groupe, texte associé bien évidemment.
Le schéma de code devient alors le suivant. Ce qui change vraiment ? Les lignes 28 à 39.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
{Fin de remplissage de la liste}
procedure
TForm15.LinkListControlToField1FilledList(Sender: TObject);
var
Pied : TListViewItem;
begin
// Ajout de la dernière totalisation (= fin de fichier)
with
ListView1.Items.Add do
begin
//texte du dernier pied de groupe
Text :='Pied de groupe'
;
Purpose := TListItemPurpose.Footer;
end
;
end
;
{Fin de remplissage d'un élément de la liste}
procedure
TForm15.LinkListControlToField1FilledListItem(Sender: TObject;
const
AEditor: IBindListEditorItem);
var
AnItem : TListViewItem;
AListItemBitmap : TListItemImage;
AListItemTextFixe : TListItemText;
AColor : TAlphaColor;
begin
AnItem:=AEditor.CurrentObject as
TListViewItem; // Item créé
if
(AnItem.Purpose=TListItemPurpose.Header) then
begin
// Ajout du Pied de Groupe si ce n'est pas le premier groupe
if
(AnItem.Index
>1
) then
begin
with
ListView1.Items.Add do
begin
// cet item n'a plus besoin d'être déplacé vers le haut
// et sera l'entête du nouveau groupe
// je récupère l'entête de groupe de l'élément créé automatiquement
Text := AnItem.text;
Purpose := TListItemPurpose.Header;
end
;
// L'item créé automatiquement devient le pied de groupe
AnItem.Purpose:=TListItemPurpose.Footer;
// on y met le texte de pied de groupe
AnItem.Text:='Pied de groupe'
end
;
// mémorisations
// initialisations
end
else
begin
// Ajout des libellés fixes
// Gestion de la couleur
// Cumuls
… ;
end
;
end
;
VI. Conclusion▲
Les LiveBindings ne font pas tout. Si avec les besoins simples on a bien du zéro code, il faut parfois ajouter son grain de sel. Très peu documentés, utilisés et même connus, les événements sur les liaisons offrent des possibilités intéressantes et c'est ce que ce tutoriel a tenté de montrer.
Mon seul regret est de ne pas avoir su utiliser l'événement OnFillingListItem dans ce cadre. Un bémol : au chapitre III.C.1, j'émettais des doutes sur le comportement des totaux en cas de modification d'un montant… Ces doutes sont confirmés, car, sauf rafraîchissement de l'ensemble des données, en cas de modification d'une donnée, si les modifications s'appliquent à l'élément détail, elles ne sont pas répercutées sur les cumuls du groupe. Pareillement, si une recherche filtre les éléments d'un groupe, cela n'a aucune incidence sur l'affichage du pied de groupe. En bref, il y a quand même des limites !
Mes remerciements aux relecteurs techniques gvasseur58 et orthographiques f-leb qui ont mis la touche finale à ce tutoriel.
Vous pourrez télécharger les sources des deux projets à cette adresse.