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

}