Reputation: 1332
I have a ListBox
with binding to a list of strings. I want to filter the list when I enter text in a TextBox
. How can I do it?
public void ListLoad()
{
ElementList = new List<string>(); // creation a list of strings
ElementList.Add("1"); // add a item of string
ElementList.Add("2"); // add a item of string
DataContext = this; // set the data context
}
I'm binding it in XAML with:
ItemsSource="{Binding ElementList}"
Upvotes: 18
Views: 28554
Reputation: 1176
Here is a more complete example using CollectionViewSource
:
XAML :
<Stackpanel>
<TextBox x:Name="TextBoxFilter" TextChanged="TextBoxFilter_TextChanged" />
<!-- Bind to the ICollectionView -->
<ListBox ItemsSource="{Binding ItemsView, RelativeSource={RelativeSource AncestorType=local:CustomUserControl}}">
</Stackpanel>
The code behind :
public class Item
{
public string Name { get; set; } = "";
}
public class CustomUserControl : UserControl
{
public List<Item> Items { get; private set; }
public ICollectionView ItemsView { get; private set; }
public CustomUserControl()
{
InitializeComponent();
Items = [
new Item() { Name = "Item 1" },
new Item() { Name = "Item 2" },
new Item() { Name = "Item 3" }
];
ItemsView = CollectionViewSource.GetDefaultView(Items);
// Filter based on the TextBox value
ItemsView.Filter = (o) => { return ((Item)o).Name.ToLower().Contains(TextBoxFilter.Text.Trim().ToLower()); };
}
// Refresh the filter
private void TBox_FiltreChoixDll_TextChanged(object sender, TextChangedEventArgs e)
{
ItemsView.Refresh();
}
}
Upvotes: 0
Reputation: 3348
CollectionViewSource class can help here. As far as I can tell it has many capabilities to filter, sort and group collections.
ICollectionView view = CollectionViewSource.GetDefaultView(ElementList);
view.Filter = (o) => {return o;}//here is the lambda with your conditions to filter
When you don't need any filter just set view.Filter
to null
.
Also check out this article on filtering
Upvotes: 38
Reputation: 17580
Here is an attached property for binding a filter:
using System;
using System.Windows;
using System.Windows.Controls;
public static class Filter
{
public static readonly DependencyProperty ByProperty = DependencyProperty.RegisterAttached(
"By",
typeof(Predicate<object>),
typeof(Filter),
new PropertyMetadata(default(Predicate<object>), OnByChanged));
public static void SetBy(ItemsControl element, Predicate<object> value)
{
element.SetValue(ByProperty, value);
}
public static Predicate<object> GetBy(ItemsControl element)
{
return (Predicate<object>)element.GetValue(ByProperty);
}
private static void OnByChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is ItemsControl itemsControl &&
itemsControl.Items.CanFilter)
{
itemsControl.Items.Filter = (Predicate<object>)e.NewValue;
}
}
}
Used like this in xaml:
<DataGrid local:Filter.By="{Binding Filter}"
ItemsSource="{Binding Foos}">
...
And viewmodel:
public class ViewModel : INotifyPropertyChanged
{
private string filterText;
private Predicate<object> filter;
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<Foo> Foos { get; } = new ObservableCollection<Foo>();
public string FilterText
{
get { return this.filterText; }
set
{
if (value == this.filterText) return;
this.filterText = value;
this.OnPropertyChanged();
this.Filter = string.IsNullOrEmpty(this.filterText) ? (Predicate<object>)null : this.IsMatch;
}
}
public Predicate<object> Filter
{
get { return this.filter; }
private set
{
this.filter = value;
this.OnPropertyChanged();
}
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private bool IsMatch(object item)
{
return IsMatch((Foo)item, this.filterText);
}
private static bool IsMatch(Foo item, string filterText)
{
if (string.IsNullOrEmpty(filterText))
{
return true;
}
var name = item.Name;
if (string.IsNullOrEmpty(name))
{
return false;
}
if (filterText.Length == 1)
{
return name.StartsWith(filterText, StringComparison.OrdinalIgnoreCase);
}
return name.IndexOf(filterText, 0, StringComparison.OrdinalIgnoreCase) >= 0;
}
}
Upvotes: 11
Reputation: 2016
If you set Dictionary as itemsource to listbox use the below code to sort,
private void tb_filter_textChanged(object sender, TextChangedEventArgs e)
{
Dictionary<string, string> dictObject = new Dictionary<string, string>();
ICollectionView view = CollectionViewSource.GetDefaultView(dictObject);
view.Filter = CustomerFilter;
listboxname.ItemsSource = view;
}
private bool CustomerFilter(object item)
{
KeyValuePair<string, string> Items = (KeyValuePair<string,string>) item;
return Items.Value.ToString().Contains("a");
}
The above code returns the items contaning "a".
Upvotes: 2