WPF

Quick Tips to Filter ListView in WPF

WPF ListView does not support Filtering by default. ListView does not have their own methods to filter the bounded list.

But WPF has a very special class CollectionViewSource which supports Filtering very effectively and in very easy way.

What is CollectionViewSource class?

CollectionViewSource is a class which we can use in WPF for filtering and sorting a bounded list. This class exist in System.Windows.

CollectionViewSource important members are:

  1. Source property – Set the source collection which we want to bind to CollectionView.
  2. View property – Set this property to ItemsSource property of ListView.
  3. Filter event – Where we can provide the filtering logic
  4. Refresh method – Reapply the filtering logic

WPF ListView Filter example in MVVM

In the below Xaml, I have bind TextBox with FilterText and bind ListView to SourceCollection property.

<Window x:Class="ListViewFiltering.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="ListView Filter" Height="350" Width="525">
    <DockPanel LastChildFill="True">
        <TextBox DockPanel.Dock="Top" Text="{Binding FilterText, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Stretch" />
        <ListView ItemsSource="{Binding SourceCollection}">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Name}" />
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </DockPanel>
</Window>

using System.Collections.ObjectModel;
using System.Windows.Data;
using System.ComponentModel;

namespace ListViewFiltering
{
    public class MainViewModel : INotifyPropertyChanged
    {
        private string filterText;
        private CollectionViewSource usersCollection;
        public event PropertyChangedEventHandler PropertyChanged;

        public MainViewModel()
        {
            ObservableCollection<User> users = new ObservableCollection<User>();
            users.Add(new User { Name = "Michael" });
            users.Add(new User { Name = "George" });
            users.Add(new User { Name = "Hilary" });
            users.Add(new User { Name = "James" });
            users.Add(new User { Name = "Chang" });
            users.Add(new User { Name = "Brian" });
            users.Add(new User { Name = "Tony" });

            usersCollection = new CollectionViewSource();
            usersCollection.Source = users;
            usersCollection.Filter += usersCollection_Filter;
        }

        public ICollectionView SourceCollection
        {
            get
            {
                return this.usersCollection.View;
            }
        }

        public string FilterText
        {
            get
            {
                return filterText;
            }
            set
            {
                filterText = value;
                this.usersCollection.View.Refresh();
                RaisePropertyChanged("FilterText");
            }
        }

        void usersCollection_Filter(object sender, FilterEventArgs e)
        {
            if (string.IsNullOrEmpty(FilterText))
            { 
                e.Accepted = true;
                return;
            }

            User usr = e.Item as User;
            if (usr.Name.ToUpper().Contains(FilterText.ToUpper()))
            {
                e.Accepted = true;
            }
            else
            {
                e.Accepted = false;
            }
        }


        public void RaisePropertyChanged(string propertyName)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }

    public class User
    {
        public string Name { get; set; }
    }
}

First I have created a list of users in the constructor. Then, I have initialized userCollection which is CollectionViewSource type and set it’s Source property to the List of Users and bind its event Filter with userCollection_Filter method.

SourceCollection property is returning the View property of CollectionViewSource which returns the bounded list of users.

usersCollection_Filter method will run for every binded item. Filter method has parameter FilterEventArgs which has below two members:

  1. Accepted: If set true, Item will pass the filter and show in ListView. If set false, Item will not appear in ListView.
  2. Item: Give you the Bounded Item

Every time Refresh method is called or CollectionViewSource is used first time, It will call the Filter method for every bounded item.

if (usr.Name.ToUpper().Contains(FilterText.ToUpper()))
{
	e.Accepted = true;
}
else
{
	e.Accepted = false;
}

In the above Filter method code, we put a condition if bounded item Name property contains the FilterText text then it will pass the filter criteria and we set the e.Accepted to true if not then set to false.

Whenever we set the FilterText, it call the Refresh method of View property of CollectionViewSource.

WPF ListView Filter Example
WPF ListView Filter Example 2