IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Composants FMX Delphi Discussion :

Filtrer une listView


Sujet :

Composants FMX Delphi

  1. #1
    Rédacteur/Modérateur

    Avatar de SergioMaster
    Homme Profil pro
    Développeur informatique retraité
    Inscrit en
    Janvier 2007
    Messages
    15 046
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 67
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur informatique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Janvier 2007
    Messages : 15 046
    Points : 40 962
    Points
    40 962
    Billets dans le blog
    62
    Par défaut Filtrer une listView
    Bonjour,

    Je veux filtrer une listview mais uniquement sur l'élément text de l'item or cela ne semble pas être le comportement standard.

    Dans mon cas , j'ai une liste view avec en Text le nom du client et en Detail Ville+Pays (ItemAppearance = ImageListItmeBottonDetail)

    j'ai mis le code suivant dans un TEdit (NomSearch) de recherche
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    procedure TMainClients.NomSearchChange(Sender: TObject);
    begin
    if Length(NomSearch.Text)>0 then
      begin
           Listview1.Items.Filter:=function(X: string): Boolean
                                           begin
                                               Result:= X.StartsWith(NomSearch.Text,true);
                                           end;
      end
    else Listview1.Items.Filter:=nil;
    end;
    problème X de la fonction semble être aussi bien la valeur liée (via livebindings) à Item.Text que à Item.Detail

    est-il possible de faire une distinction ? C'est peut-être ma manière de faire le filtre qui déconne ?
    Note : pour l'heure, je peux toujours passer par caractère invisible (espace, espace insécable ...) en début de la partie détail par exemple mais je suis étonné qu'il n'y ait pas une solution plus 'simple', genre une propriété non trouvée. Mais si je voulais quelque chose comme "le client dont le nom contient" (posex) au lieu de "le client dont le nom commence par" (startswith) je serais marron
    MVP Embarcadero
    Delphi installés : D3,D7,D2010,XE4,XE7,D10 (Rio, Sidney), D11 (Alexandria), D12 (Athènes)
    SGBD : Firebird 2.5, 3, SQLite
    générateurs États : FastReport, Rave, QuickReport
    OS : Window Vista, Windows 10, Windows 11, Ubuntu, Androïd

  2. #2
    Expert confirmé
    Avatar de Ph. B.
    Homme Profil pro
    Freelance
    Inscrit en
    Avril 2002
    Messages
    1 784
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 57
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Freelance
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2002
    Messages : 1 784
    Points : 5 915
    Points
    5 915
    Par défaut
    Bonjour,
    Citation Envoyé par SergioMaster Voir le message
    problème X de la fonction semble être aussi bien la valeur liée (via livebindings) à Item.Text que à Item.Detail
    Eh bien si j'en crois l'aide de XE8, c'est le cas :
    Obtient ou définit une fonction (filtre) générique qui détermine les éléments de la liste TListView pour l'inclusion dans la vue.

    Cette propriété est un référence à une fonction générique qui se comporte comme un prédicat logique (voir TPredicate) recevant une valeur de chaîne et renvoie une valeur booléenne. Cette fonction sera appelée pour chaque texte de l'élément et chaque texte de détail de l'élément pour déterminer les éléments à afficher sur l'écran.
    L'aide de Seattle est plus évasive
    Prédicat basé sur les chaînes qui évalue une chaîne à partir d'un élément de liste et renvoie True si la chaîne passe le filtre, ou False dans le cas contraire. Les éléments de liste qui ne passent pas le filtre ne sont pas affichés sur la vue liste.

    L'implémentation de DoFilterItem dans les sous-classes de TFilterableListViewItems est responsable de la mise en vigueur du filtre spécifié. Généralement, DoFilterItem détermine les chaînes des éléments de liste que le filtre évalue. Toutefois, les implémentations dans les sous-classes peuvent définir une logique de filtrage qui ne prend pas en compte la valeur de Filter.
    Cela dit, son code correspondant l'est beaucoup moins !
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    // Unité FMX.ListView.Appearances, ligne 4834
    // Concrete filter
    function TAppearanceListViewItems.DoFilterItem(const Item: TListItem): Boolean;
    begin
      Result := Filter(TListViewItem(Item).Text) or Filter(TListViewItem(Item).Detail);
    end;
    Citation Envoyé par SergioMaster Voir le message
    est-il possible de faire une distinction ? C'est peut-être ma manière de faire le filtre qui déconne ?
    Honnêtement, je ne sais pas... et la dernière phrase (soulignée) de l'aide de Seattle me laisse perplexe.

    Citation Envoyé par SergioMaster Voir le message
    Note : pour l'heure, je peux toujours passer par caractère invisible (espace, espace insécable ...) en début de la partie détail par exemple mais je suis étonné qu'il n'y ait pas une solution plus 'simple', genre une propriété non trouvée. Mais si je voulais quelque chose comme "le client dont le nom contient" (posex) au lieu de "le client dont le nom commence par" (startswith) je serais marron
    Et en passant par un filtre au niveau de l'ensemble de données sous jacent ?
    Philippe.

  3. #3
    Rédacteur/Modérateur

    Avatar de SergioMaster
    Homme Profil pro
    Développeur informatique retraité
    Inscrit en
    Janvier 2007
    Messages
    15 046
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 67
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur informatique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Janvier 2007
    Messages : 15 046
    Points : 40 962
    Points
    40 962
    Billets dans le blog
    62
    Par défaut
    Bonjour,

    Merci Philippe de passer par là
    Je confirme, l'aide de Seattle est vraiment "étrange" quoique ... dans mon cas il s'agit d'une listView plutôt "classique" (texte+détail sur une ligne) dans le cas d'éléments customisés (style, texte de bouton etc...) je n'ose imaginer

    voilà le truc : j'ai donc une table "éloignée" que je rapatrie dans une table mémoire
    with Datas do
    begin
    FDQClients.Active:=True; // FDQClients la table sur le serveur
    FDQClients.FetchAll;
    FDClients.Data:=Datas.FDQClients.Data; // FDClients la table mémoire (FDMemTable)
    FDQClients.Active:=False;
    je rempli alors ma liste via LiveBindings à partir de FDClients (donc jusque là pas de souci)

    Ensuite c'est là que ça se corse
    si j'utilise la propriété de ListView à savoir SearchVisible=True (avec SearchAlwaysOnTop) la recherche ("case sensitive") se fait bien et ce uniquement sur le Item.text
    mais moi , je veux une recherche "case insensitive" et je voudrais que le EditBox de recherche ne soit pas en haut de la liste mais en bas dans un panel (je simplifie), je vais mettre une image écran pour que cela soit parlant
    Donc j'ai mis mon TEdit (d'ailleurs j'aurais bien voulu un TSearchBox à la place, mais j'ai beau cherché je ne vois pas le composant , donc à moins de le créer au runtime ....)

    Nom : ListViewSearch.PNG
Affichages : 1254
Taille : 27,8 Ko

    Avec ma petite astuce, code de mon premier post, j'ai le même comportement (si on excepte le case sensitive) en haut et en bas (en bas c'est même mieux )

    Et en passant par un filtre au niveau de l'ensemble de données sous jacent ?
    j'ai essayé de faire une sélection à l'intérieur de l'ensemble de données par SQLLocal (une première pour moi) mais les temps sont plus que décevants
    c'est long ! autant la recherche dans la listView (750 clients environ) se fait de manière quasi instantanée autant le SQLLocal prend du temps

    après la nuit passée à ruminer sur le sujet (outre l'astuce des caractères non visibles) je vais tenter, ce matin, d'autres solutions.
    comme par exemple : Filtrer le DataSet, j'avais complétement oublié qu'avec Firedac c'est possible

    Pour finir : Je n'avais pas réussi à trouver le code de la fonction DoFilterItem, encore merci de l'avoir trouvé pour moi, le nez dans le guidon (sources) au bout d'un moment on ne voit plus rien je m'étais donc mis en pause , du coup peut être est-il possible de l'overrider bien qu'elle soit strict protected
    MVP Embarcadero
    Delphi installés : D3,D7,D2010,XE4,XE7,D10 (Rio, Sidney), D11 (Alexandria), D12 (Athènes)
    SGBD : Firebird 2.5, 3, SQLite
    générateurs États : FastReport, Rave, QuickReport
    OS : Window Vista, Windows 10, Windows 11, Ubuntu, Androïd

  4. #4
    Rédacteur/Modérateur

    Avatar de SergioMaster
    Homme Profil pro
    Développeur informatique retraité
    Inscrit en
    Janvier 2007
    Messages
    15 046
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 67
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur informatique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Janvier 2007
    Messages : 15 046
    Points : 40 962
    Points
    40 962
    Billets dans le blog
    62
    Par défaut Quelques petits bémols mais
    que j'étais en allant titiller le SQLLocal, le Filter de la FDMemTable convient très bien à mon projet, en modifiant quelque peu la structure de ma table mémoire (pour la future recherche Ville,Code Postal,Pays du second TEdit) je vais m'en sortir

    En lieu et place d'un filtre sur la ListView (code en commentaire) j'ai utilisé les propriétés Filter et Filtered de la table mémoire
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    procedure TMainClients.NomSearchChange(Sender: TObject);
    begin
    if Length(NomSearch.Text)>0 then
      begin
     {      Listview1.Items.Filter:=function(X: string): Boolean
           begin
              Result:= X.StartsWith(NomSearch.Text,true);
           end; }
         Datas.FDClients.Filtered:=false;
         Datas.FDClients.Filter:='NOM LIKE '+QuotedStr(UpperCase(NomSearch.Text)+'%');
         Datas.FDClients.Filtered:=True;
      end
    else begin
       {Listview1.Items.Filter:=nil;}
       Datas.FDClients.Filtered:=False;
    end;
    end;
    Juste un bémol : Après le filtre, si je vois bien les éléments suivants, je ne vois plus les éléments précédents
    je m'explique si je demande les clients dont le nom commence par 'BAR' le filtre fait son travail , j'efface ensuite la demande (donc filtered:=false) la liste ne contient aucun 'A......' comme si la liste ne se remplissait qu'à partir de l'enregistrement courant
    j'ai donc été obligé de rajouter un Datas.FDClients.First en fin de procédure

    me reste "plus qu'à" travailler sur un expression régulière pour la seconde partie du filtre mais je considère que c'est si toutefois quelqu'un sait comment surclasser la fonction OnDoFilterItem ou une manière simple d'utiliser TSearchBox je suis toujours preneur
    MVP Embarcadero
    Delphi installés : D3,D7,D2010,XE4,XE7,D10 (Rio, Sidney), D11 (Alexandria), D12 (Athènes)
    SGBD : Firebird 2.5, 3, SQLite
    générateurs États : FastReport, Rave, QuickReport
    OS : Window Vista, Windows 10, Windows 11, Ubuntu, Androïd

  5. #5
    Rédacteur/Modérateur

    Avatar de SergioMaster
    Homme Profil pro
    Développeur informatique retraité
    Inscrit en
    Janvier 2007
    Messages
    15 046
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 67
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur informatique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Janvier 2007
    Messages : 15 046
    Points : 40 962
    Points
    40 962
    Billets dans le blog
    62
    Par défaut Cette fois ci vraiment résolu
    Bonjour,

    encore confronté à ce problème cette fois j'ai fait différemment, le temps de filtrage sur les données étant trop long.

    je livre le bébé tel quel

    j'ai ajouté une variable privé booléenne ItemDetail, puis j'ai codé l'évènement OnFilter de la listview qui contient je le rappelle Text et Detail, de la manière suivante
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
     
    ...
    private 
     ItemDetail : Boolean
    ...
     
    procedure TForm2.FormCreate(Sender: TObject);
    begin
     ItemDetail:=False;
     ...
    end; 
     
    procedure TForm2.ListView1Filter(Sender: TObject; const AFilter, AValue: string;
      var Accept: Boolean);
    var Valeur : String;
    begin
    Valeur:=AValue.Trim.ToLower;
    if Nom.IsChecked   // case à cocher qui m'indique si je doit faire la recherche sur le texte ou sur le détail
       then Accept:= ((not ItemDetail) AND Valeur.StartsWith(AFilter,True)) OR AFilter.IsEmpty // recherche sur item.text
       else Accept:= (ItemDetail AND Valeur.Contains(AFilter.Trim.ToLower)) OR AFilter.IsEmpty; // recherche dans item.detail
    if Accept then Itemdetail:=false
                 else ItemDetail:=not ItemDetail;
    end;
    MVP Embarcadero
    Delphi installés : D3,D7,D2010,XE4,XE7,D10 (Rio, Sidney), D11 (Alexandria), D12 (Athènes)
    SGBD : Firebird 2.5, 3, SQLite
    générateurs États : FastReport, Rave, QuickReport
    OS : Window Vista, Windows 10, Windows 11, Ubuntu, Androïd

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [C#] [WinForms] Redimensionnement automatique d'une ListView
    Par Phenomenium dans le forum Windows Forms
    Réponses: 2
    Dernier message: 18/02/2005, 11h43
  2. Changer dynamiquement la couleur d'un item d'une listview
    Par little_cypress dans le forum C++Builder
    Réponses: 2
    Dernier message: 29/11/2004, 14h46
  3. [VB.NET] Objet .Tag dans une ListView
    Par Lois dans le forum Windows Forms
    Réponses: 7
    Dernier message: 12/10/2004, 16h35
  4. [C#] Surlignage d'un élement d'une ListView
    Par tontonplaisir dans le forum Windows Forms
    Réponses: 9
    Dernier message: 08/09/2004, 15h35
  5. [VB6] Supprimer un enregistrement dans une ListView ??
    Par Argonz dans le forum VB 6 et antérieur
    Réponses: 2
    Dernier message: 14/11/2002, 09h37

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo