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
- Anonymous
December 12, 2007
Due to customer feedback and requests, I've been working on an article that demonstrates a simple implementation