Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF Filter a ListBox

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}"
like image 712
Karl_Schuhmann Avatar asked Mar 12 '13 10:03

Karl_Schuhmann


3 Answers

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

like image 104
Blablablaster Avatar answered Nov 20 '22 19:11

Blablablaster


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;
    }
}
like image 35
Johan Larsson Avatar answered Nov 20 '22 20:11

Johan Larsson


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".

like image 1
Joee Avatar answered Nov 20 '22 20:11

Joee