IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Voir le flux RSS

Blog de Serge Girard (aka SergioMaster)

[Dephi][VCL]Carnet de plongées : TControlList, Jour 3 - Nouvelle plongée

Noter ce billet
par , 10/04/2021 à 10h43 (3314 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.

Nom : Capture_1.PNG
Affichages : 33
Taille : 183,0 Ko

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;
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
procedure TMainForm.ControlListManuelleButtonClick(Sender: TObject);
begin
FDPlayList.RecNo:=ControlListManuelle.ItemIndex+1;  // positionnement sur l'enregistrement
PageControl1.ActivePage:=Paroles;
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 .

Nom : Capture_3.PNG
Affichages : 19
Taille : 264,8 Ko

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.
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;
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
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;
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
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;
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.

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
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;
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 TForm18.ControlList1EnableControl(const AIndex: Integer;
  AControl: TControl; var AEnabled: Boolean);
begin
if AControl is TControlListButton
   then  AEnabled:=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é.

Qu'en est-il pour la liste cochée ? Une simple instruction supplémentaire dans l'évènement BeforeDrawItem fera l'affaire
Code : Sélectionner tout - Visualiser dans une fenêtre à part
ListView1.Items[Controllist1.ItemIndex].Checked:=True;
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.
Bien sûr il est possible de coder ainsi pour l'éviter
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;
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é

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.
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;
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 ?).

Nom : Capture_4.PNG
Affichages : 20
Taille : 81,9 Ko
*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.

Envoyer le billet « [Dephi][VCL]Carnet de plongées : TControlList, Jour 3 - Nouvelle plongée » dans le blog Viadeo Envoyer le billet « [Dephi][VCL]Carnet de plongées : TControlList, Jour 3 - Nouvelle plongée » dans le blog Twitter Envoyer le billet « [Dephi][VCL]Carnet de plongées : TControlList, Jour 3 - Nouvelle plongée » dans le blog Google Envoyer le billet « [Dephi][VCL]Carnet de plongées : TControlList, Jour 3 - Nouvelle plongée » dans le blog Facebook Envoyer le billet « [Dephi][VCL]Carnet de plongées : TControlList, Jour 3 - Nouvelle plongée » dans le blog Digg Envoyer le billet « [Dephi][VCL]Carnet de plongées : TControlList, Jour 3 - Nouvelle plongée » dans le blog Delicious Envoyer le billet « [Dephi][VCL]Carnet de plongées : TControlList, Jour 3 - Nouvelle plongée » dans le blog MySpace Envoyer le billet « [Dephi][VCL]Carnet de plongées : TControlList, Jour 3 - Nouvelle plongée » dans le blog Yahoo

Catégories
Programmation , Delphi

Commentaires