Mit der CollectionViewSource
können Elemente in ListBoxen
, ListViews und anderen grafischen GUI-Elementen leicht umgesetzt werden.
Eine ObservableCollection
lässt sich an ein ListView
zur Darstellung der Elemente binden. Doch wie kann man die Liste filtern? Hier steht mit der CollectionViewSource
ein Proxy zur Verfügung, welcher uns dies erlaubt. Dir binden die Liste nicht mehr direkt an das GUI-Element, sondern die CollectionViewSource
.
Schauen wir unser Ziel an:
Eine Liste von Elementen. Über ein Suchfeld wollen wir über den Anfang des Namens oder der favorisierten Biersorte filtern.
Wie gehen wir vor? Hinweis: Das ganze Projekt gibt es am Ende zum Download.
Fangen wir mit der Liste der Personen an:
private ObservableCollection_listPersons.Add(new Person() { Name = "Ebe", FavoriteBeer = "Augustiner" }); _listPersons.Add(new Person() { Name = "Andy", FavoriteBeer = "Öttinger" }); _listPersons.Add(new Person() { Name = "Herbie", FavoriteBeer = "Reischenau Gold" }); _listPersons.Add(new Person() { Name = "Erwin", FavoriteBeer = "Heineken" }); _listPersons.Add(new Person() { Name = "Wolfram", FavoriteBeer = "Dein Bier" }); _listPersons.Add(new Person() { Name = "Klaus", FavoriteBeer = "Radeberger" }); _listPersons.Add(new Person() { Name = "Kiwi", FavoriteBeer = "Meisterpils" }); _listPersons.Add(new Person() { Name = "Peter", FavoriteBeer = "Dein Bier" });_listPersons = new ObservableCollection ();
Die Listen binden wir nicht direkt an unser ListView
, sondern erstellen eine CollectionViewSource
:
internal CollectionViewSource PersonViewSource { get; set; } = new CollectionViewSource();
Für die Datenbindung im Xaml-Code legen wir ein Property für den Zugriff auf das View-Element der CollectionViewSource
an:
public ICollectionView PersonView { get { return PersonViewSource.View; } }
Nun verbinden wir die CollectionViewSource
mit unserer Liste der Daten, dies machen wir im Konstruktor unseres Fensters:
PersonViewSource.Source = _listPersons;
Im Xaml-Code legen wir jetzt unser LiewView
an, für die Datenbindung verwenden wir unser Property PersonView
:
<ListView x:Name="ListViewPerson" Grid.Column="1" ItemsSource="{Binding PersonView}"> <ListView.ItemTemplate> <DataTemplate> <StackPanel Orientation="Horizontal"> <TextBlock Text="{Binding Name}"></TextBlock> <TextBlock Text=" - "></TextBlock> <TextBlock Text="{Binding FavoriteBeer}"></TextBlock> </StackPanel> </DataTemplate> </ListView.ItemTemplate>
Das Ergebnis ist, dass wir nun bereits unsere Liste angezeigt bekommen sollten.
Filtern der Anzeige
Soweit so gut, aber nun wollen wir die Anzeige filtern. Dazu legen wir ein Property an:
private string _filterText = ""; public string FilterText { get { return _filterText; } set { _filterText = value; NotifyPropertyChanged(); PersonView.Refresh(); } }
Dies ist unser Suchtext nach dem wir filtern wollen. In der GUI legen wir dazu eine Textbox an:
<TextBox x:Name="TextBoxSearch" Text="{Binding FilterText, UpdateSourceTrigger=PropertyChanged}" ></TextBox>
Ändert der Anwender nun den Suchtext, soll unsere PersonView
einen Refresh auslösen. Was noch fehlt ist nun die Logik für die Filterung. Hierzu bietet die CollectionViewSource
ein Event mit dem Namen Filter an.
PersonViewSource.Filter += PersonViewSource_Filter;
Die Implementierung:
private void PersonViewSource_Filter(object sender, FilterEventArgs e) { if (string.IsNullOrWhiteSpace(_filterText)) { //no filter when no search text is entered e.Accepted = true; } else { Person p = (Person)e.Item; if (p.Name.StartsWith(_filterText) || p.FavoriteBeer.StartsWith(_filterText)) { e.Accepted = true; } else { e.Accepted = false; } } }
Der Filter geht über die Liste der Elemente und bei jedem Element können wir entscheiden, ob es angezeigt wird. In unserem Beispiel wollen wir, dass alle Elemente angezeigt werden, wenn der Anwender keinen Suchtext eingegeben hat. Hat er etwas eingegeben, dann wird geschaut, ob der String als Starttext von Name
oder FavoriteBeer
vorkommt.
Das Ergebnis ist eine automatische Filterung. Der Vorteil: unsere unterliegende Liste bleibt komplett unbehelligt davon.