Simplest way to filter generic list
I have the following class that defines an entry in my table (I have remove the constructor for brewity, it simple sets that data):
class FilterResult
{
public bool Checked { get; set; }
public string Url { ge开发者_如何学运维t; private set;}
public string Description { get; private set; }
public int ItemID { get; private set; }
}
I am using this as a simple source for a DataGridView in a WinForms application, created using the 'Choose Data Source' in the GUI, which resulted in a automatically created class called filterResultBindingSource
.
Now, this approach does not support the DataGridView.Filter property, and I need to be able to do a simple filter on the results.
How can I, in the fastest/easiest way possible, create a custom list, binding source or something third to support filtering? From what I've seen, there's a lot of work involved in implementing IBindingListView or a new BindingSource, but correct me if I'm wrong.
It does not necessarily have to use the Filter property, in fact it would probably be easier if I could implement a custom method where I can filter using the Where
-method available on the list.
Check out the BindingListView project over at sourceforge.net. This project is fairly easy to use and the download includes useful samples.
However, for your fast and easy to implement solution look at the BindListView class (no relation to the sourceforge project) presented here (specifically listing 9.7). This is a generic class that you can set as your BindingSource's DataSource and filter through the BindingSource's Filter property. There are some restrictions on how you can use it that are discussed in the article I linked but for me it's been perfect for the quick solution you're looking for.
I'll add that you can use both solutions to allow sorting in your DGV.
EDIT: The link I used to get the BindingListView class a couple years ago was dead so I found it somewhere else. I decided to go ahead and post the code below for future reference in case this link also disappears.
public class BindingListView<T> : BindingList<T>, IBindingListView, IRaiseItemChangedEvents
{
private bool m_Sorted = false;
private bool m_Filtered = false;
private string m_FilterString = null;
private ListSortDirection m_SortDirection = ListSortDirection.Ascending;
private PropertyDescriptor m_SortProperty = null;
private ListSortDescriptionCollection m_SortDescriptions = new ListSortDescriptionCollection();
private List<T> m_OriginalCollection = new List<T>();
public BindingListView()
: base() {
}
public BindingListView(List<T> list)
: base(list) {
}
protected override bool SupportsSearchingCore {
get { return true; }
}
protected override int FindCore(PropertyDescriptor property,
object key) {
// Simple iteration:
for (int i = 0; i < Count; i++) {
T item = this[i];
if (property.GetValue(item).Equals(key)) {
return i;
}
}
return -1; // Not found
// Alternative search implementation
// using List.FindIndex:
//Predicate<T> pred = delegate(T item)
//{
// if (property.GetValue(item).Equals(key))
// return true;
// else
// return false;
//};
//List<T> list = Items as List<T>;
//if (list == null)
// return -1;
//return list.FindIndex(pred);
}
protected override bool SupportsSortingCore {
get { return true; }
}
protected override bool IsSortedCore {
get { return m_Sorted; }
}
protected override ListSortDirection SortDirectionCore {
get { return m_SortDirection; }
}
protected override PropertyDescriptor SortPropertyCore {
get { return m_SortProperty; }
}
protected override void ApplySortCore(PropertyDescriptor property,
ListSortDirection direction) {
m_SortDirection = direction;
m_SortProperty = property;
SortComparer<T> comparer = new SortComparer<T>(property, direction);
ApplySortInternal(comparer);
}
private void ApplySortInternal(SortComparer<T> comparer) {
if (m_OriginalCollection.Count == 0) {
m_OriginalCollection.AddRange(this);
}
List<T> listRef = this.Items as List<T>;
if (listRef == null)
return;
listRef.Sort(comparer);
m_Sorted = true;
OnListChanged(new ListChangedEventArgs(
ListChangedType.Reset, -1));
}
protected override void RemoveSortCore() {
if (!m_Sorted)
return; Clear();
foreach (T item in m_OriginalCollection) {
Add(item);
}
m_OriginalCollection.Clear();
m_SortProperty = null;
m_SortDescriptions = null;
m_Sorted = false;
}
void IBindingListView.ApplySort(ListSortDescriptionCollection sorts) {
m_SortProperty = null;
m_SortDescriptions = sorts;
SortComparer<T> comparer = new SortComparer<T>(sorts);
ApplySortInternal(comparer);
}
string IBindingListView.Filter {
get {
return m_FilterString;
}
set {
m_FilterString = value;
m_Filtered = true;
UpdateFilter();
}
}
void IBindingListView.RemoveFilter() {
if (!m_Filtered)
return;
m_FilterString = null;
m_Filtered = false;
m_Sorted = false;
m_SortDescriptions = null;
m_SortProperty = null;
Clear();
foreach (T item in m_OriginalCollection) {
Add(item);
}
m_OriginalCollection.Clear();
}
ListSortDescriptionCollection IBindingListView.SortDescriptions {
get {
return m_SortDescriptions;
}
}
bool IBindingListView.SupportsAdvancedSorting {
get {
return true;
}
}
bool IBindingListView.SupportsFiltering {
get {
return true;
}
}
protected virtual void UpdateFilter() {
int equalsPos = m_FilterString.IndexOf('=');
// Get property name
string propName = m_FilterString.Substring(0, equalsPos).Trim();
// Get filter criteria
string criteria = m_FilterString.Substring(equalsPos + 1,
m_FilterString.Length - equalsPos - 1).Trim();
// Strip leading and trailing quotes
criteria = criteria.Substring(1, criteria.Length - 2);
// Get a property descriptor for the filter property
PropertyDescriptor propDesc = TypeDescriptor.GetProperties(typeof(T))[propName];
if (m_OriginalCollection.Count == 0) {
m_OriginalCollection.AddRange(this);
}
List<T> currentCollection = new List<T>(this);
Clear();
foreach (T item in currentCollection) {
object value = propDesc.GetValue(item);
if (value.ToString() == criteria) {
Add(item);
}
}
}
bool IBindingList.AllowNew {
get {
return CheckReadOnly();
}
}
bool IBindingList.AllowRemove {
get {
return CheckReadOnly();
}
}
private bool CheckReadOnly() {
if (m_Sorted || m_Filtered) {
return false;
} else {
return true;
}
}
protected override void InsertItem(int index, T item) {
foreach (PropertyDescriptor propDesc in
TypeDescriptor.GetProperties(item)) {
if (propDesc.SupportsChangeEvents) {
propDesc.AddValueChanged(item, OnItemChanged);
}
}
base.InsertItem(index, item);
}
protected override void RemoveItem(int index) {
T item = Items[index];
PropertyDescriptorCollection propDescs = TypeDescriptor.GetProperties(item);
foreach (PropertyDescriptor propDesc in propDescs) {
if (propDesc.SupportsChangeEvents) {
propDesc.RemoveValueChanged(item, OnItemChanged);
}
}
base.RemoveItem(index);
}
void OnItemChanged(object sender, EventArgs args) {
int index = Items.IndexOf((T)sender);
OnListChanged(new ListChangedEventArgs(
ListChangedType.ItemChanged, index));
}
bool IRaiseItemChangedEvents.RaisesItemChangedEvents {
get { return true; }
}
}
class SortComparer<T> : IComparer<T>
{
private ListSortDescriptionCollection m_SortCollection = null;
private PropertyDescriptor m_PropDesc = null;
private ListSortDirection m_Direction =
ListSortDirection.Ascending;
public SortComparer(PropertyDescriptor propDesc,
ListSortDirection direction) {
m_PropDesc = propDesc;
m_Direction = direction;
}
public SortComparer(ListSortDescriptionCollection sortCollection) {
m_SortCollection = sortCollection;
}
int IComparer<T>.Compare(T x, T y) {
if (m_PropDesc != null) // Simple sort
{
object xValue = m_PropDesc.GetValue(x);
object yValue = m_PropDesc.GetValue(y);
return CompareValues(xValue, yValue, m_Direction);
} else if (m_SortCollection != null &&
m_SortCollection.Count > 0) {
return RecursiveCompareInternal(x, y, 0);
} else return 0;
}
private int CompareValues(object xValue, object yValue,
ListSortDirection direction) {
int retValue = 0;
if (xValue is IComparable) {
retValue = ((IComparable)xValue).CompareTo(yValue);
} else if (yValue is IComparable) {
retValue = ((IComparable)yValue).CompareTo(xValue);
}
// not comparable, compare String representations
else if (!xValue.Equals(yValue)) {
retValue = xValue.ToString().CompareTo(yValue.ToString());
}
if (direction == ListSortDirection.Ascending) {
return retValue;
} else {
return retValue * -1;
}
}
private int RecursiveCompareInternal(T x, T y, int index) {
if (index >= m_SortCollection.Count)
return 0; // termination condition
ListSortDescription listSortDesc = m_SortCollection[index];
object xValue = listSortDesc.PropertyDescriptor.GetValue(x);
object yValue = listSortDesc.PropertyDescriptor.GetValue(y);
int retValue = CompareValues(xValue,
yValue, listSortDesc.SortDirection);
if (retValue == 0) {
return RecursiveCompareInternal(x, y, ++index);
} else {
return retValue;
}
}
}
I think the easiest way, using a generic List object, is to convert it to a DataTable.
public Form1( )
{
InitializeComponent( );
List<Item> list = new List<Item>( );
list.Add( new Item( 1, "ITEM 001" ) );
list.Add( new Item( 2, "ITEM 002" ) );
list.Add( new Item( 3, "ITEM 003" ) );
list.Add( new Item( 4, "ITEM 004" ) );
list.Add( new Item( 5, "ITEM 005" ) );
list.Add( new Item( 6, "ITEM 006" ) );
list.Add( new Item( 7, "ITEM 007" ) );
list.Add( new Item( 100, "SW-RED" ) );
list.Add( new Item( 101, "SW-BLUE" ) );
list.Add( new Item( 102, "SW-GREEN" ) );
list.Add( new Item( 103, "SW-BLACK" ) );
list.Add( new Item( 104, "SW-WHITE" ) );
// convert list to table
DataTable dt = new DataTable( );
dt = Convert.ToDataTable<Item>( list );
DataView view = dt.AsDataView( );
view.RowFilter = "ItemID < 100";
comboBox1.DataSource = view;
comboBox1.ValueMember = "ItemID";
comboBox1.DisplayMember = "Description";
}
精彩评论