Filtering Code



using System;


using System.Collections.Generic;


using System.Text;


using System.ComponentModel;


using System.Windows.Forms;


using System.Text.RegularExpressions;


using System.Reflection;


using System.Collections;


 


public class SimpleFilteredList<T> : BindingList<T>, IBindingListView


{


    public SimpleFilteredList() {}


 



#region Searching


protected override bool SupportsSearchingCore


{


    get


    {


       return true;


    }


}


protected override int FindCore(PropertyDescriptor prop, object key)


{


    return FindCore(0, prop, key);


}


protected int FindCore(int startIndex, PropertyDescriptor prop, object key)


{


    // Get the property info for the specified property.


    PropertyInfo propInfo = typeof(T).GetProperty(prop.Name);


    T item;


    if (key != null)


    {


    // Loop through the items to see if the key


    // value matches the property value.


        for (int i = startIndex; i < Count; ++i)


        {


             item = (T)Items[i];


             if (propInfo.GetValue(item, null).Equals(key))


             return i;


        }


     }


    return -1;


}


public int Find(int startIndex, string property, object key)


{


    // Check the properties for a property with the specified name.


    PropertyDescriptorCollection properties =


    TypeDescriptor.GetProperties(typeof(T));


    PropertyDescriptor prop = properties.Find(property, true);


    // If there is not a match, return -1 otherwise pass search to


    // FindCore method.


    if (prop == null)


    return -1;


   else


   if (startIndex <= 0)


    return FindCore(prop, key);


   else


   return FindCore(startIndex, prop, key);


}


#endregion Searching


#region Sorting


ArrayList sortedList;


ArrayList unsortedItems;


bool isSortedValue;


ListSortDirection sortDirectionValue;


PropertyDescriptor sortPropertyValue;


protected override bool SupportsSortingCore


{


    get { return true; }


}


protected override bool IsSortedCore


{


    get { return isSortedValue; }


}


protected override PropertyDescriptor SortPropertyCore


{


     get { return sortPropertyValue; }


}


protected override ListSortDirection SortDirectionCore


{


     get { return sortDirectionValue; }


}


 


public void ApplySort(string propertyName, ListSortDirection direction)


{


    // Check the properties for a property with the specified name.


    PropertyDescriptor prop = TypeDescriptor.GetProperties(typeof(T))[propertyName];


    // If there is not a match, return -1 otherwise pass search to


    // FindCore method.


    if (prop == null)


    throw new ArgumentException(propertyName +


    ” is not a valid property for type:” + typeof(T).Name);


    else


    ApplySortCore(prop, direction);


}


protected override void ApplySortCore(PropertyDescriptor prop,


ListSortDirection direction)


{


    RaiseListChangedEvents = false;


    sortedList = new ArrayList();


    // Check to see if the property type we are sorting by implements


    // the IComparable interface.


    Type interfaceType = prop.PropertyType.GetInterface(“IComparable”);


    if (interfaceType != null)


    {


         // If so, set the SortPropertyValue and SortDirectionValue.


         sortPropertyValue = prop;


         sortDirectionValue = direction;


         unsortedItems = new ArrayList(this.Count);


         // Loop through each item, adding it the the sortedItems ArrayList.


         foreach (Object item in this.Items)


         {


              sortedList.Add(prop.GetValue(item));


              unsortedItems.Add(item);


         }


         // Call Sort on the ArrayList.


         sortedList.Sort();


         T temp;


         // Check the sort direction and then copy the sorted items


         // back into the list.


         if (direction == ListSortDirection.Descending)


         sortedList.Reverse();


         for (int i = 0; i < this.Count; i++)


         {


             int position = Find(0, prop.Name, sortedList[i]);


             if (position != i)


             {


                  temp = this[i];


                  this[i] = this[position];


                  this[position] = temp;


             }


       }


      RaiseListChangedEvents = true;


      isSortedValue = true;


      // Raise the ListChanged event so bound controls refresh their


      // values.


      OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));


  }


  else


  // If the property type does not implement IComparable, let the user


  // know.


  throw new NotSupportedException(“Cannot sort by “ + prop.Name +


  “. This” + prop.PropertyType.ToString() +


  ” does not implement IComparable”);


}


protected override void RemoveSortCore()


    {


     int position;


    object temp;


    RaiseListChangedEvents = false;


    // Ensure the list has been sorted.


    if (unsortedItems != null)


    {


          // Loop through the unsorted items and reorder the


         // list per the unsorted list.


        for (int i = 0; i < unsortedItems.Count; )


         {


             position = this.Find(0, SortPropertyCore.Name,


             unsortedItems[i].GetType().


             GetProperty(SortPropertyCore.Name).GetValue(unsortedItems[i], null));


             if (position >= 0 && position != i)


             {


                   temp = this[i];


                   this[i] = this[position];


                   this[position] = (T)temp;


                   i++;


              }


              else if (position == i)


               i++;


              else


             // If an item in the unsorted list no longer exists, delete it.


             unsortedItems.RemoveAt(i);


        }


        RaiseListChangedEvents = true;


        OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));


    }


}


public void RemoveSort()


{


     RemoveSortCore();


}


public override void EndNew(int itemIndex)


{


    // Check to see if the item is added to the end of the list,


    // and if so, re-sort the list.


    if (sortPropertyValue != null && itemIndex > 0 && itemIndex == this.Count – 1)


         ApplySortCore(this.sortPropertyValue, this.sortDirectionValue);


     base.EndNew(itemIndex);


}


#endregion Sorting


#region AdvancedSorting


public bool SupportsAdvancedSorting


{


    get { return false; }


}


public ListSortDescriptionCollection SortDescriptions


{


    get { return null; }


}


public void ApplySort(ListSortDescriptionCollection sorts)


{


     throw new NotImplementedException();


}


#endregion AdvancedSorting


#region Filtering


private string filterValue = “”;


/// <summary>


/// Should be in the format columnName =’desiredValue’


/// </summary>


private string filterPropertyNameValue;


private Object filterCompareValue;


List<T> unfilteredListValue = new List<T>();


public List<T> UnfilteredList


{


    get { return unfilteredListValue; }


}


public bool SupportsFiltering


{


     get { return true; }


}


 


public void RemoveFilter()


{


     if (Filter != null) Filter = null;


}


public string FilterPropertyName


{


     get { return filterPropertyNameValue; }


}


public Object FilterCompare


{


    get { return filterCompareValue; }


}


 


public string Filter


{


    get


    {


        return filterValue;


    }


    set


    {


        if (filterValue != value)


        {




  RaiseListChangedEvents = false;


  // If filter value is null, reset list.


  if (value == null)


  {


      this.ClearItems();


      foreach (T t in unfilteredListValue)


          this.Items.Add(t);


      filterValue = value;


   }


   // If the value is empty string, do nothing.


   // This behavior is compatible with DataGridView


   // AutoFilter code.


   else if (value == “”){}


 


   // If the value is not null or string, than process


   // normal.


   else if (Regex.Matches(value,


    “[?[\\w ]+]? ?[=] ?’?[\\w|/: ]+’?”,


    RegexOptions.Singleline).Count == 1 && value != “”)


   {


      // If the filter is not set.


       unfilteredListValue.Clear();


       unfilteredListValue.AddRange(this.Items);


       filterValue = value;


       GetFilterParts();


       ApplyFilter();


     }


     else if (Regex.Matches(value,


       “[?[\\w ]+]? ?[=] ?’?[\\w|/: ]+’?”,


       RegexOptions.Singleline).Count > 1)


           throw new ArgumentException(“Multi-column” +


             “filtering is not implemented.”);


    else throw new ArgumentException(“Filter is not” +


           “in the format: propName = ‘value’.”);


RaiseListChangedEvents = true;


OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));


}


}


}   


void FilteredListView_ListChanged(object sender, ListChangedEventArgs e)


{


   MessageBox.Show(RaiseListChangedEvents.ToString());


   // Add the new item


   if (e.ListChangedType == ListChangedType.ItemAdded)


      unfilteredListValue.Add(this[e.NewIndex]);


  // Remove the new item


   if (e.ListChangedType == ListChangedType.ItemDeleted)


     unfilteredListValue.RemoveAt(e.NewIndex);


}


 


private void ApplyFilter()


{


    unfilteredListValue.Clear();


    unfilteredListValue.AddRange(this.Items);


    List<T> results = new List<T>();


    PropertyDescriptor propDesc = TypeDescriptor.GetProperties(typeof(T))[FilterPropertyName];


    if (propDesc != null)


    {


        int tempResults = -1;


       do


       {


            tempResults = FindCore(tempResults + 1, propDesc, FilterCompare);


            if (tempResults != -1)


            results.Add(this[tempResults]);


       } while (tempResults != -1);


    }


   this.ClearItems();


   if (results != null && results.Count > 0)


   {


        foreach (T itemFound in results)


        this.Add(itemFound);


   }


}


 


public void GetFilterParts()


{


    string[] filterParts = Filter.Split(new char[] { ‘=’ }, StringSplitOptions.RemoveEmptyEntries);


    filterPropertyNameValue = filterParts[0].Replace(“[“, “”).Replace(“]”, “”).Trim();


    PropertyDescriptor propDesc =


    TypeDescriptor.GetProperties(typeof(T))[filterPropertyNameValue.ToString()];


    if (propDesc != null)


   {


       try


       {


            TypeConverter converter = TypeDescriptor.GetConverter(propDesc.PropertyType);


             filterCompareValue = converter.ConvertFromString(filterParts[1].Replace(“‘”, “”).Trim());


       }


      catch (NotSupportedException)


      {


          throw new ArgumentException(“Specified filter value “ +


          FilterCompare + ” can not be converted from string.” +


              “..Implement a type converter for “ + propDesc.PropertyType.ToString());


       }


}


    else throw new ArgumentException(“Specified property ‘” +


       FilterPropertyName + “‘ is not found on type “ +


       typeof(T).Name + “.”);


    }


#endregion Filtering


}

Comments (1)

  1. Due to customer feedback and requests, I’ve been working on an article that demonstrates a simple implementation