[Dephi][VCL]Carnet de plongées : TControlList, Jour 3 - Nouvelle plongée
par
, 10/04/2021 à 10h43 (4379 Affichages)
Un nouveau tour sur le spot, autrement écrit, avec le fichier "biolife".
Mais tout d'abord, allons-y en musique (uniquement les paroles, j'aurais certes préféré mettre des extraits sons mais ma "musicologie" ne va pas si loin ).
Ceci pour me rassurer sur les images . En effet ce Stream.position:=8 du billet "un peu d'apnée" était loin de me plaire !
Pour vérifier ça j'ai utilisé une base de données SQLite existante, la base chinook.db (un équivalent SQLite à la base employee.gdb d'Interbase). Pour les besoins d'un de mes tutoriels sur les Livebindings FMX j'avais "dopée" cette base standard en y ajoutant quelques extraits texte de chansons et de couvertures d'albums ce qui correspond donc à mon besoin.
Reste à obtenir les paroles (par appui sur le bouton associé), dans le cas de la liste en remplissage manuel l'astuce est de jouer sur le numéro d'enregistrement ainsi la liaison livebindings restera identique quelque soit la liste utilisée.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12 // Remplissage par programmation procedure TMainForm.ControlListManuelleBeforeDrawItem(AIndex: Integer; ACanvas: TCanvas; ARect: TRect; AState: TOwnerDrawState); begin FDplayList.RecNo:=AIndex+1; lblAlbum2.Caption:=FDPlayListName.asString; LblTitre2.Caption:=FDPlayListTitle.AsString; var mStream := TmemoryStream.Create; TBlobField(FDPlayListPochette).SaveToStream(mStream); mStream.Position:=0; // ouf ! il s'agit bien du type de format particulier dans le fichier biolife.xml Image2.Picture.LoadFromStream(mStream); end;
L'objectif ce cette plongée : étudier le comportement du composant TControlList avec des mises à jour de données ou une autre liste, mais aussi voir comment gérer l'affichage de contrôles au sein d'un élément (évènements OnEnableControl, OnDrawControl). Pour ce faire j'ai décidé de faire une sorte d'interface d'inventaire. Chaque fois qu'un poisson sera vu le plongeur le cliquera dans sa liste (bien sûr il aura le droit à l'erreur), une liste supplémentaire récapitulera les poissons vu au moins une fois, et, au cours des paliers de décompression, il lui sera possible de faire de la traduction histoire de passer le temps .
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6 procedure TMainForm.ControlListManuelleButtonClick(Sender: TObject); begin FDPlayList.RecNo:=ControlListManuelle.ItemIndex+1; // positionnement sur l'enregistrement PageControl1.ActivePage:=Paroles; end;
Vous noterez que j'ai pris le parti d'utiliser les LiveBindings partout (même pour les zones d'édition). C'est un parti pris, le TControlList nécessitant les LiveBindings, il me semblait non nécessaire d'ajouter un TDataSource afin d'utiliser des composants liés aux données (TDBEdit, TDBMemo, TDBNavigator) et ce même si cela m'a fait soulever un premier petit bogue de version.
Le premier challenge de ce programme : Comment mémoriser les vues sans pour autant modifier la table existante ou ajouter un table supplémentaire ?
Pour ce faire, j'ai utilisé un TDictionary (classe déclarée dans l'unité System.Generics.Collections), déclaré dans la section private de la forme, elle sera initialisée dès la création.
Donc, dans le principe, quand le plongeur/utilisateur cliquera sur la fiche du poisson (évènement OnItemClick) on indiquera cela au niveau du Compte et l'on répercutera ensuite cela sur le ControlList.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9 ... private { Déclarations privées } Compte : TDictionary<integer,Cardinal>; .... procedure TFormInventaire.FormCreate(Sender: TObject); begin Compte:=TDictionary<integer,Integer>.Create; end;
Pour afficher le nombre de vue (lblcount) , comme déjà vu lors d'un précédent billet, l'utilisation de l'évènement OnBeforeDrawItem fera l'affaire.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10 procedure TFormInventaire.ControlList1ItemClick(Sender: TObject); var nombre : integer; begin .. if Compte.TryGetValue(Controllist1.ItemIndex,Nombre) then nombre:=nombre+1 else nombre:=1; Compte.AddOrSetValue(Controllist1.ItemIndex,nombre); ControlList1.Refresh; // rafraichir la liste end;
Prenez bien garde, tous les éléments visibles sont redessinés pas uniquement l'élément "actif" faites donc en sorte de prévoir les deux cas.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8 procedure TFormInventaire.ControlList1BeforeDrawItem(AIndex: Integer; ACanvas: TCanvas; ARect: TRect; AState: TOwnerDrawState); var nombre : Cardinal; begin if Compte.TryGetValue(AIndex,nombre) then lblcount.Caption:=Nombre.ToString else lblCount.Caption:=Emptystr; end;
Il serait également possible de gérer l'affichage du bouton "droit à l'erreur" au sein de cette procédure mais d'autres évènements existent : OnShowControl,OnEnableControl
Si l'on veut cacher le bouton on utilisera plutôt OnShowControl
Si l'on veut simplement le désactiver OnEnableControl
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6 procedure TFormInventaire.ControlList1ShowControl(const AIndex: Integer; AControl: TControl; var AVisible: Boolean); begin if AControl is TControlListButton then AVisible:=Compte.ContainsKey(AIndex); end;
Et s'il y avait plusieurs fois le même type de contrôle ? Pas de souci, il est toujours possible de tester le nom i.e if Sametext(AControl.Name,'controllistbutton1') then ... quelque soit le nombre d'éléments affichés ce sera le même nom ce qui peut être déstabilisant quand on n'y est pas habitué.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6 procedure TForm18.ControlList1EnableControl(const AIndex: Integer; AControl: TControl; var AEnabled: Boolean); begin if AControl is TControlListButton then AEnabled:=Compte.ContainsKey(AIndex); end;
Qu'en est-il pour la liste cochée ? Une simple instruction supplémentaire dans l'évènement BeforeDrawItem fera l'affaire
Si vous avez reproduit mon code, au lancement du programme vous ne pourrez que le constater, il y a quelque chose qui cloche dès que l'ajout de ce code.
Code : Sélectionner tout - Visualiser dans une fenêtre à part ListView1.Items[Controllist1.ItemIndex].Checked:=True;
Bien sûr il est possible de coder ainsi pour l'éviter
Un effet presque similaire est visible si l'on utilise le navigateur pour changer l'enregistrement en cours, le nombre de vue est incrémenté
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2 if ListView1.Items.Count=0 then exit; try ListView1.Items[Controllist1.ItemIndex].Checked:=True; except end;
La racine du mal est profonde et à mon avis est intrinsèque au composant TControlList, l'évènement OnItemClick est levé à chaque positionnment dans le dataset !
La solution pour le programme : Utiliser l'évènement OnItemDblClick à la place.
Pour le droit à l'erreur, je code l'évènement OnClick du TControlListButton de façon à décrémenter la paire correspondant à l'élément.
Pour finir sur une note agréable, la partie traduction, de fait la partie modification des données et implications que je voulais tester, se fait sans aucune ligne de code grâce aux LiveBindings (fallait-il le préciser ?).
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11 procedure TFormInventaire.ControlListButton1Click(Sender: TObject); var nombre : cardinal; begin if Compte.TryGetValue(Controllist1.ItemIndex,nombre) then Compte.AddOrSetValue(Controllist1.ItemIndex,nombre-1); if nombre-1<=0 then begin Compte.Remove(Controllist1.ItemIndex); ListView1.Items[Controllist1.ItemIndex].Checked:=False; // décoché si 0 end; end;
*petite cerise sur le gâteau, le composant TControlList semble répondre parfaitement aux changements d'apparences (ici 'Iceberg Classico')
Juste une chose, si vous utilisez le fichier biolife.xml et que vous voulez que vos données soient modifiées, n'oubliez pas de sauvegarder le fichier à la clôture du programme.