get the solution

Blog

Martin Martin
10.12.2009 12:03

Filter Data mit MVVM (Filtering ListView, ListBox mit MVVM)



In diesem Eintrag zeige ich, wie man in WPF Daten (IList<T>) mit dem MVVM Pattern filtern kann.
Ich habe darauf geachtet, dass man möglichst individuell bestimmen kann wie Daten gefiltert werden können. Natürlich gibt es noch andere Möglichkeiten Listen zu filtern (CollectionView) die ihre Vor- und Nachteile gegenüber dieser Methode haben. Seht selbst ob sie euch gefällt.

Im Prinzip ist dieser Lösungsvorschlag relativ einfach zu realisieren. Im ViewModel hat man wie üblich die Liste mit den Daten auf die eine ListBox, ListView... bindet. Wenn wir nun aber die Liste filtern wollen, benötigen wir zusätzlich eine weitere Liste in dem nur die gefilterten Daten gespeichert sind. Das Control zum Anzeigen der Daten bindet nun auf die Gefilterte Liste mit den Daten und nicht mehr auf die orginal Liste. Die gefilterte Liste macht folgendes.
 
Sie führt eine Linq Abfrage auf die Original Liste aus. Zu dieser Linq Abfrage fügt man sämtliche Bedinungen ein die dem Filter entsprechen. Schlussendlich wird aus der Linq Abfrage die gefilterte Liste erstellt, welche dann eine ListBox, ListView… anzeigt.

Um diese Vorgehensweise besser veranschaulichen zu können habe ich ein Beispiel erstellt. In diesem Beispiel sollen sämtliche Produkte eines Geschäftes angezeigt werden. Damit der Kunde eine bessere Übersicht über die Produkte hat, soll man einen Filter hinzufügen.
 
Einmal soll der Kunde nach Produkten suchen können und diese auch nach dem Preis und dem Attribut Ausverkauft filtern können.
Unser Produkt hat die Eigenschaften Name, Preis, Ausverkauft.
 
In Unserem ViewModel haben wir einmal die Liste mit den Orginal Daten (ProduktList) und eine Liste welche nur die Gefilterten Daten zurück gibt (FilteredList). Zusätzlich haben wir noch pro Filter eine Propertie die den Filter representiert. Also FilterAusverkauft, FilterName zum nach Produkten suchen zu können und FilterPreis um nur Produkte mit einem bestimmten Preis an zu zeigen.

 
ref

 
Zuerst der XAML Code. Die Oberfläche zeigt einmal eine ListView an, in der die gefilterten Produkte je nach Filter Einstellung angezeigt werden. Die ListView bindet auf die Gefilterte Liste vom ViewModel. Zusätzlich hat der Benutzer die Möglickeit die Filter mittels TextBox, CheckBox zu setzen.

	            <GroupBox Header="Filter" DockPanel.Dock="Top">
	                <StackPanel Orientation="Horizontal">
	                    <!-- Hier kann man die Filter aktivieren -->
	                    <TextBlock Text="Name" VerticalAlignment="Top" />
	                    <TextBox Text="{Binding Path=FilterName,UpdateSourceTrigger=PropertyChanged}"/>
	                    <TextBlock Text="Preis" VerticalAlignment="Top" />
	                    <TextBox Text="{Binding Path=FilterPreis,UpdateSourceTrigger=PropertyChanged}"/>
	                    <TextBlock Text="Ausverkauft" VerticalAlignment="Top" />
	                    <CheckBox IsChecked ="{Binding Path=FilterAusverkauft,UpdateSourceTrigger=PropertyChanged}"/>
	                </StackPanel>
	            </GroupBox>
	            <!-- Daten anzeigen -->
	            <ListView ItemsSource="{Binding Path=FilteredList,UpdateSourceTrigger=PropertyChanged}" DockPanel.Dock="Top">
	                <ListView.View>
	                    <GridView>
	                        <GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}"></GridViewColumn>
	                        <GridViewColumn Header="Preis" DisplayMemberBinding="{Binding Preis}"></GridViewColumn>
	                        <GridViewColumn Header="Ausverkauft" DisplayMemberBinding="{Binding Ausverkauft}"></GridViewColumn>
	                    </GridView>
	                </ListView.View>
	            </ListView>

Wichtig ist, dass man bei den Filter Properties beim Binding ein UpdateSourceTrigger=PropertyChanged an gibt. Dann werden sämtliche aktualisierungen sofort weiter gegeben. Wenn jetzt jemand beim Filter Name einen Buchstaben eingibt, wird sofort die GefilterteListe erneuert.

	        public string FilterName
	        {
	            get
	            {
	                return _FilterName;
	            }
	            set
	            {
	                _FilterName = value;
	                NotifyPropertyChanged("FilterName");
	                NotifyPropertyChanged("FilteredList");
	            }
	        }
 
Beim Propertie FilterName wird zusätzlich die Methode NotifyPropertyChanged("FilteredList") ausgeführt. Was zur Folge hat, dass die ListView sich nochmal die Daten von der Propertie FiltedList holt.

	        public ObservableCollection<Produkt> FilteredList
	        {
	            get
	            {
	                return GetFilteredList(ProduktList);
	            }
	        }

Dabei wird unsere GetFiltedList Methode aufgerufen. Dieser übergeben wir die orginal Liste. Diese führt die Linq Abfrage aus und erstellt uns eine neue gefilterte Liste.

	        private ObservableCollection<Produkt> GetFilteredList(ObservableCollection<Produkt> pOrginalProduktList)
	        {
	            ObservableCollection<Produkt> filteredproduktlist = new ObservableCollection<Produkt>();
	 
	            var x = from p in pOrginalProduktList
	                    where
	                        FilterNameMethod(p, this.FilterName) &&
	                        FilterAusverkauftMethod(p, this.FilterAusverkauft) &&
	                        FilterPreisMethod(p, this.FilterPreis)
	                    select p;
	 
	            foreach (var u in x)
	                filteredproduktlist.Add(u);
	 
	            return filteredproduktlist;
	        }

Der Linq Abfrage übergeben wir unsere Bedingungs Methoden FilterNameMethode, FilterAusverkauftMethode, FilterPreisMethode. In dieser können wir selbst bestimmen wie wir was überprüfen. Schauen wir uns dazu die FilterNameMethode etwas genauer an.

	        public static bool FilterNameMethod(Produkt pProdukt, string pName)
	        {
	            if (pProdukt == null) return false;
	            if (pName == null) return false;
	            if (pName.Equals(string.Empty)) return true;
	            return (pProdukt.Name.ToUpper().StartsWith(pName.ToUpper()));
	        }

Die Bedinungsmethode muss immer einen Boolean zurückgeben. Sinnvoll ist es auch, das gerade itterierende Item der Linq Abfrage der Methode zu übergen. Als zweiten Parameter übergeben wir die FilterName Propertie. Damit können wir die Linq Abfrage beeinflussen.

In der dritten if Bedinung wird überprüft ob pName (=FilterName) leer ist. Wir geben hier true zurück. Das Element soll also in die gefilterte Liste hinzugefügt werden, da der Filter nicht aktiv scheint. Erst wenn jemand in die Propertie Filtername bzw. in pName etwas hineinschreibt soll je nach dem, wenn der Anfangsbuchstabe passt, das gerade itterierende Item zur gefilterten Liste hinzugefügt werden.

Die Vorgangsweise bei den anderen Filter Methoden ist die gleiche. Statt einer String Überprüfung wird ein int oder bool Wert geprüft. Soll das Item in die gefilterte Liste aufgenommen werden gibt die Filter Methode true zurück andern falls false.

Will man ein Element Manipulieren, löschen, duplizieren etc. muss man die Änderungen immer an der Orginal Liste durchführen. Wichig dabei ist, dass nach der Änderung ein NotifyPropertyChanged("FilterList") ausgeführt wird, damit die FiltedList neu erstellt wird.

Ich denke, dass man dieses Filter Prinzip relativ einfach umsetzen kann. Bei unklarheiten, ergänzungen schreibt mir ein Comment. Das gesamte Projekt kann vom Anhang heruntergeladen werden.



Beispielprojekt Download:

 

kick it on dotnet-kicks.de

Schlüsselwörter: WPF, XAML, LINQ
zuletzt geändert: 06. April 2010 14:29
Link zu diesem Artikel: (in die Zwischenablage)





(c) 2011 | Impressum |

| Empfehlenswerte Blog Einträge