Laden...

MVVM-ListBox mit TextBox-Inhalt durchsuchen/Filtern

Erstellt von Nexmo vor 2 Jahren Letzter Beitrag vor 2 Jahren 354 Views
N
Nexmo Themenstarter:in
36 Beiträge seit 2021
vor 2 Jahren
MVVM-ListBox mit TextBox-Inhalt durchsuchen/Filtern

Guten Morgen zusammen.
Ich hänge nun seit ein paar Tagen vor folgendem Problem:

ich habe eine ObservableCollection<Klasse> name welche ich als ItemSource an eine ListBox gebunden habe.

nun möchte ich über eine TextBox diese Liste Filtern.

Im Netz habe ich da verschiedene Ansätze schon gefunden, leider jedoch alle nur als CodeBehind, oder als Win-Form-style.
beides bringt mich jedoch nicht weiter.

Die ObservableCollection wird aus einer Datenbank heraus gefüllt.

mein Ansatz war jetzt mit ICollectionView zu arbeiten, jedoch funktioniert das leider nicht wie gewünscht.

Könnte mir da bitte jemand weiterhelfen?

Auszug aus dem XAML


 <GroupBox Header="Suchen" Grid.Row="0" Margin="5,5,5,5" Background="AliceBlue">
                <TextBox TextWrapping="Wrap" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="5,0,0,0" Width="200" Text="{Binding Path=Filter, UpdateSourceTrigger=PropertyChanged}"/>
            </GroupBox>
            <GroupBox Header="Gefundene Personen" Grid.Row="1" Margin="5,5,5,5">
                <ListBox Margin="5,5,5,5" ItemsSource="{Binding Personendaten}" SelectedItem="{Binding PersData}"  >

Auszug aus dem MainWindowViewModel


using Microsoft.Data.Sqlite;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;

namespace MVVM_Adressdatenbank
{
    class MainWindowViewModel : NotifyableBaseObject
    {

        public MainWindowViewModel()
        {
            this.Abfrage = new DelegateCommand((o) => { Personendaten.Clear(); Datenabfrage(); });
            this.weitereAbfrage = new(

                (o) => { Kontaktdaten.Clear(); KontaktDatenabfrage(PersData.ID); AdressDatenabfrage(PersData.ID); });
            this.Speichern = new DelegateCommand((o) => { Datenänderungspeichern(); });
            this.AdresseSpeichern = new((o) => { Adressänderungspeichern(); });

        }

        public bool HasNonZeroValue => PersData.Vorname != null;
        

        private ObservableCollection<PersonenDaten> personendaten = new ObservableCollection<PersonenDaten>();

        public ObservableCollection<PersonenDaten> Personendaten
        {
            get => personendaten;
            set
            {
                if (personendaten != value)
                {
                    personendaten = value;
                    RaisePropertyChanged();
                    
                }
            }
        }

        public ICollectionView phrasesView; 
        public void AnagramViewModel()
        {         
            phrasesView = CollectionViewSource.GetDefaultView(Personendaten);
            phrasesView.Filter = o => string.IsNullOrEmpty(Filter) || ((string)o).Contains(Filter);
        }
        string filter;
        public string Filter
        {
            get => filter;
            set
            {
                if (value!=filter)
                {
                    filter = value;
                    //RaisePropertyChanged();
                    RaisePropertyChanged(nameof(AnagramViewModel));
                }
            }
        }
       
       
        private ObservableCollection<KontaktDaten> kontaktdaten = new ObservableCollection<KontaktDaten>();
        public ObservableCollection<KontaktDaten> Kontaktdaten
        {
            get => kontaktdaten;
            set
            {
                if (kontaktdaten != value)
                {
                    kontaktdaten = value;
                    RaisePropertyChanged();
                   
                }
            }
        }

       

        KontaktDaten kontData = new KontaktDaten();
        public KontaktDaten KontData
        {
            get => kontData;
            set
            {
                if (kontData != value)
                {
                    kontData = value;
                    RaisePropertyChanged();
                }
            }
        }
        PersonenDaten persData = new PersonenDaten();
        public PersonenDaten PersData
        {
            get => persData;
            set
            {
                if (persData != value)
                {
                    persData = value;
                    RaisePropertyChanged();
                    RaisePropertyChanged(nameof(HasNonZeroValue));
                    
                    
                }
            }
        }

        public DelegateCommand Abfrage
        {
            get; set;
        }

        public DelegateCommand weitereAbfrage { get; set; }

        public DelegateCommand Speichern { get; set; }
        public DelegateCommand AdresseSpeichern { get; set; }

        public event EventHandler Safesucces;

        Datenbankzugriff zugriff = Datenbankzugriff.GetDBZugriff();

        private void Datenabfrage()
        {
            
            SqliteConnection connect = zugriff.connect();
            //string datenabfrage = "Select ID_Person, Vorname, Nachname, Geburtsdatum, Geburtsname FROM Person";
            string datenabfrage = "Select p.ID_Person, a.ID_Adresse, Vorname, Nachname, Geburtsname, Geburtsdatum, Straße, Hausnummer, Postleitzahl, Ort, Land, Typ_Name From Person p JOIN Person_Adresse pa ON " +
                "p.ID_Person = pa.ID_Person " +
                "JOIN Adresse a ON pa.ID_Adresse = a.ID_Adresse " +
                "JOIN Adress_Typ at ON pa.ID_Adress_Typ = at.ID_Adress_Typ ";
                
            SqliteCommand com = new(datenabfrage, connect);

            connect.Open();
            SqliteDataReader reader = com.ExecuteReader();
            if (reader.HasRows)
                while (reader.Read())
                {
                    Personendaten.Add(new PersonenDaten
                    {
                        ID = reader.GetString(reader.GetOrdinal("ID_Person")),
                        Vorname = reader.GetString(reader.GetOrdinal("Vorname")),
                        Nachname = reader.GetString(reader.GetOrdinal("Nachname")),
                        Geburtsdatum = reader.GetString(reader.GetOrdinal("Geburtsdatum")),
                        Geburtsname = reader.GetString(reader.GetOrdinal("Geburtsname")),
                        Straße = reader.GetString(reader.GetOrdinal("Straße")),
                        Hausnummer = reader.GetString(reader.GetOrdinal("Hausnummer")),
                        Plz = reader.GetString(reader.GetOrdinal("Postleitzahl")),
                        Ort = reader.GetString(reader.GetOrdinal("Ort")),
                        Land = reader.GetString(reader.GetOrdinal("Land")),
                        AdressTyp = reader.GetString(reader.GetOrdinal("Typ_Name")),
                        ID_Adresse=reader.GetString(reader.GetOrdinal("ID_Adresse"))
                    });


                
                }


            connect.Close();
        }

}


4.931 Beiträge seit 2008
vor 2 Jahren

Du mußt dann für die ICollectionView eine eigene Eigenschaft erzeugen und diese dann an die ListBox binden (statt Personendaten). Und diese muß dann entweder die komplette Liste oder aber die gefilterten Daten liefern, je nach Filter.

PS: Der Name Personendaten ist ungünstig gewählt, da du ihn sowohl in der Einzahl als auch Mehrzahl (ObservableCollection<>) benutzt.

N
Nexmo Themenstarter:in
36 Beiträge seit 2021
vor 2 Jahren

Du mußt dann für die ICollectionView eine eigene Eigenschaft erzeugen und diese dann an die ListBox binden (statt Personendaten). Und diese muß dann entweder die komplette Liste oder aber die gefilterten Daten liefern, je nach Filter.

PS: Der Name Personendaten ist ungünstig gewählt, da du ihn sowohl in der Einzahl als auch Mehrzahl (ObservableCollection<>) benutzt.

Wo verwende ich denn den Namen in der Einzahl??

Personendaten ist nur der name der ObservableCollection, welche von der klasse PersonenDaten befüllt wird.( Groß/Kleinschreibung bitte beachten)

Sollte dass dann so aussehen??


public ObservableCollection<PersonenDaten> Newlist { get; private set; }

        public ICollectionView phrasesView; 
        public void AnagramViewModel()
        {
            Newlist = Personendaten;
            phrasesView = CollectionViewSource.GetDefaultView(Newlist);
            phrasesView.Filter = o => string.IsNullOrEmpty(Filter) || ((string)o).Contains(Filter);
        }

4.931 Beiträge seit 2008
vor 2 Jahren

Leg für ICollectionView phrasesView eine Eigenschaft an und binde daran.

Und bei Filter ist RaisePropertyChanged(nameof(AnagramViewModel)) zurzeit sinnlos, da dies keine Eigenschaft (sondern eine Methode) darstellt. Hier mußt du dann den Namen der obigen Eigenschaft setzen.

PS: Ich meinte den Klassennamen PersonenDaten sowie die Eigenschaften PersData und Personendaten. Es ist aus den Namen nicht ersichtlich, was sich auf eine Person oder auf mehrere bezieht (analog für KontaktDaten).
Edit: Dazu habe ich den englischen Artikel An opinionated guide to naming your code, aimed at new developers gefunden.

N
Nexmo Themenstarter:in
36 Beiträge seit 2021
vor 2 Jahren

Leg für ICollectionView phrasesView eine Eigenschaft an und binde daran.

Und bei Filter ist RaisePropertyChanged(nameof(AnagramViewModel)) zurzeit sinnlos, da dies keine Eigenschaft (sondern eine Methode) darstellt. Hier mußt du dann den Namen der obigen Eigenschaft setzen.

PS: Ich meinte den Klassennamen PersonenDaten sowie die Eigenschaften PersData und Personendaten. Es ist aus den Namen nicht ersichtlich, was sich auf eine Person oder auf mehrere bezieht (analog für KontaktDaten).
Edit: Dazu habe ich den englischen Artikel
>
gefunden.

ah, ok.

also die Liste Personendaten bezieht sich auf eine Sammlung von Personen => PersData, welche durch die Klasse PersonenDaten bestimmt werden.

hab eben mal etwas umgestellt, da ich gemerkt habe, das AnagramViewModel der konstruktor war von dem Beispiel 😉


 public MainWindowViewModel()
        {
            this.Abfrage = new DelegateCommand((o) =>
            { Personendaten.Clear(); Datenabfrage(); });
            this.weitereAbfrage = new((o) => { Kontaktdaten.Clear(); KontaktDatenabfrage(PersData.ID); AdressDatenabfrage(PersData.ID); });
            this.Speichern = new DelegateCommand((o) => { Datenänderungspeichern(); });
            this.AdresseSpeichern = new((o) => { Adressänderungspeichern(); });
            Newlist = Personendaten;
            phrasesView = CollectionViewSource.GetDefaultView(Newlist);
            phrasesView.Filter = o => string.IsNullOrEmpty(Filter) || ((string)o).Contains(Filter);
        }

......

  public ObservableCollection<PersonenDaten> Newlist
        {
            get ; set;
        }
    

    private ICollectionView phrasesView;

    string filter;
    public string Filter
    {
        get => filter;
        set
        {
            if (value != filter)
            {
                filter = value;
                phrasesView.Refresh();
                RaisePropertyChanged(nameof(Filter));
            }
        }
    }

leider hab ich da jetzt halt das problem, dass er mir den Fehler bringt, dass er den string bei ((string)o.)contains(Filter) nicht auflösen kann, da er keinen string hat, sondern nur die Objekte aus der Collection personendaten.

glaub muss mir da einen eigenen filter schreiben, kann das sein?

4.931 Beiträge seit 2008
vor 2 Jahren

Wenn sich dein Filter auf mehrere (bzw. alle) Eigenschaften von PersonenDaten beziehen soll, dann mußt du sie einzeln auflisten:


phrasesView.Filter = p => string.IsNullOrEmpty(Filter) || p..Vorname.Contains(Filter) || p.Nachname.Contains(Filter); // || ...

(alternativ ginge auch eine Hilfsmethode, welche mittels Reflection über alle Eigenschaften iteriert)

PS: Warum nennst du die Klasse nicht einfach Person? Und die Liste dann Personen (bzw. englisch Persons).
Die Mischung von Deutsch und Englisch ist auf Dauer nicht gut für ein Projekt...

N
Nexmo Themenstarter:in
36 Beiträge seit 2021
vor 2 Jahren
GELÖST

Ich habe das Problem hinbekommen:

im Konstruktor kommt folgendes rein:


 Searchable = Personendatenliste;
            Listable = new CollectionViewSource();
            Listable.Source = this.Searchable;
            Listable.Filter += ApplyFilter;


 public ObservableCollection<PersonenDaten> Searchable { get; set; }
        internal CollectionViewSource Listable { get; set; }
        public ICollectionView GetListable { get => Listable.View; }

        private void ApplyFilter(object sender, FilterEventArgs e)
        {
            PersonenDaten search = (PersonenDaten)e.Item;
            if (string.IsNullOrEmpty(this.Filter) || this.Filter.Length == 0)
            { e.Accepted = true; }
            else
            {
                e.Accepted = search.Fullname.Contains(Filter);
                RaisePropertyChanged();
            }


        }

        private string filter;
        public string Filter
        {
            get => this.filter;
            set
            {
                if (value != filter)
                {
                    this.filter = value;
                    //RaisePropertyChanged();
                    Listable.View.Refresh();
                    RaisePropertyChanged(nameof(Filter));
                }
            }
        }

Danke an alle, die mir geholfen haben.