Using the ListCollectionView Filter method in WPF with C#

NOTE: This solution does not implement paging and requires the retrieval of the entire result set. If your dataset is huge, I wouldn’t go this route. Other options, LIKE %text%, FULL-TEXT search, Lucene.Net…and many others… Check out another of my blogs where I defer the retrieval using a Delayed Action.

Again, if you look hard enough for something you can find it. I was tasked with creating a filter on a dynamically created DataGrid in a WPF program using C#. First, finding and understanding the ListCollectionVIew was a challenge, and then came the implementation. Initial research resulted in finding most examples using a strongly typed list to populate the DataGrid and therefore searching the result set and repopulating the DataGrid was just a few lines of code.

The problem with strongly typing the data set is that your users are limited to using and accessing only that object. For example, a system which presents and captures only customer details. My requirements were to allow a user to select any column from any table, build the query, populate a DataGrid and then filter through the results. That was not a 3 or 4 liner and I wasn’t able to find many examples of this. However, when I was able to pull it off, it provided the user with an interface into the database that allowed them to view and capture data in any way they wanted.

NOTE: I had to hack a little to get the data loaded in a way that worked here. When retrieved from a real database it would be in the correct format. The ListCollectionVIew wants an IList.

The first thing to do is create some variable to store the ListCollectionVIew and the DataSet.

ListCollectionView CollectionViewList;
DataTable DataTableFiltered;

In the method where you load the data into your DataTable or GridView, load the result set into the CollectionViewList too.

CollectionViewList = new ListCollectionView(results);

The trigger for the filter is the TextChanged event of the filter text box on the WPF window. I have created a Textbox_TextChanged() method. The TextChanged() method calls the method ContainsIt() which checks each row of the DataGrid for a matching value currently typed into the filter text box. If true is returned from the ContainsIt() method, then the filter entered into the textbox on the WPF window it applied to the CollectionViewList. I.e. when applied, only the matching rows will exist in the CollectionViewList object.

private void textBox1_TextChanged(object sender, TextChangedEventArgs e)
{
     if (textBox1.Text != "")
     {
         if (CollectionViewList.CanFilter)
         {
             CollectionViewList.Filter = new Predicate <object>(ContainsIt);
 
             FilterIt();
         }
         else
         {
             CollectionViewList.Filter = null;
         }
     }
     else
     {
         CollectionViewList.Filter = null;
         FilterIt();
     }
}

The ContainsIt() method loops through each row in the DataGrid to determine if the value entered into the TextBox exists in the DataGrid row. If it does, then true is returned, if not, false.

public bool ContainsIt(object value)
{
  if (DataTableFiltered.Columns.Count > 1)
  {
    //There is more than 1 column in DataGrid
    List<string> DataGridRowList = (List<string>)value;
    foreach (object item in DataGridRowList)
    {
     if (item != null)
     {
       if(item.ToString()
              .ToLower()
              .Contains(textBox1.Text
                                .ToLower())) return true;
     }
    }
   }
   else
   {
     //There is a single column in the DataGrid
     if (value.ToString().ToLower()
              .Contains(textBox1.Text
              .ToLower())) return true;
    }
    return false;
}

Once the data has been filtered out of the CollectionViewList object, I then repopulate the DataTable with the newly filtered CollectionViewList object. This repopulation happens in the FIlterIt() method below.

public void FilterIt()
{
     int count = 0;
     DataTableFiltered.Clear();
     dataGrid1.ItemsSource = null;
 
     foreach (List<string> row in CollectionViewList)
     {
         DataTableFiltered
            .Rows
            .Add(row[0], row[1], row[2], row[3], row[4]);
         count++;
     }
 
     dataGrid1.ItemsSource = DataTableFiltered.DefaultView;
}

That’s it. Keep on coding and rockin!!!

Download the source