由于将List<T>或者BindingList<T>绑定到BindingSource时,不支持BindingSource的排序和filter方法,特此扩展BindingListView<T>,再将BindingListView<T>绑定到BindingSource,可实现以上需求。
1.首先定义BusinessObjectBase类,实现INotifyPropertyChanged接口,可以做为实体类的基类。
BusinessObjectBase : INotifyPropertyChanged
{
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
private void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (null != PropertyChanged)
{
PropertyChanged(this, e);
}
}
#endregion
}
2.定义PropertyComparer<T>类,做为排序时的方法类。
{
private ListSortDirection _direction;
private PropertyDescriptor _property;
public PropertyComparer(PropertyDescriptor property, ListSortDirection direction)
{
_property = property;
_direction = direction;
}
/// <summary>
/// 比较方法
/// </summary>
/// <param name="x">相对属性x</param>
/// <param name="y">相对属性y</param>
/// <returns></returns>
#region IComparer<T>
public int Compare(T xWord, T yWord)
{
// Get property values
object xValue = GetPropertyValue(xWord, _property.Name);
object yValue = GetPropertyValue(yWord, _property.Name);
// Determine sort order
if (_direction == ListSortDirection.Ascending)
{
return CompareAscending(xValue, yValue);
}
return CompareDescending(xValue, yValue);
}
public bool Equals(T xWord, T yWord)
{
return xWord.Equals(yWord);
}
public int GetHashCode(T obj)
{
return obj.GetHashCode();
}
#endregion
// Compare two property values of any type
private int CompareAscending(object xValue, object yValue)
{
int result;
// If values implement IComparer
if (xValue is IComparable)
{
result = ((IComparable)xValue).CompareTo(yValue);
}
// If values don't implement IComparer but are equivalent
else if (xValue == null || xValue.Equals(yValue))
{
result = 0;
}
// Values don't implement IComparer and are not equivalent, so compare as string values
else result = xValue.ToString().CompareTo(yValue.ToString());
// Return result
return result;
}
private int CompareDescending(object xValue, object yValue)
{
// Return result adjusted for ascending or descending sort order ie
// multiplied by 1 for ascending or -1 for descending
return CompareAscending(xValue, yValue) * -1;
}
private object GetPropertyValue(T value, string property)
{
// Get property
PropertyInfo propertyInfo = value.GetType().GetProperty(property);
// Return value
return propertyInfo.GetValue(value, null);
}
}
3.定义BindingListView<T>类,实现排序和过滤(Filter)
BindingListViewTest
{
public delegate void FilterHandler(object Sender, ref bool Include);
[Serializable]
public class BindingListView<T> : BindingList<T>, IBindingListView, ITypedList
{
private List<PropertyComparer<T>> comparers;
private FilterHandler mFilterHandler;
private string mFilterString = string.Empty;
[NonSerialized]
private PropertyDescriptorCollection properties;
private ArrayList unfilteredItems = new ArrayList();
public BindingListView()
{
// Get the 'shape' of the list.
// Only get the public properties marked with Browsable = true.
PropertyDescriptorCollection pdc = TypeDescriptor.GetProperties(
typeof(T),
new Attribute[]
{
new BrowsableAttribute(true)
});
// Sort the properties.
properties = pdc.Sort();
}
public BindingListView(IList<T> list)
: base(list)
{
// Get the 'shape' of the list.
// Only get the public properties marked with Browsable = true.
PropertyDescriptorCollection pdc = TypeDescriptor.GetProperties(typeof(T),
new Attribute[]
{
new BrowsableAttribute(true)
});
// Sort the properties.
properties = pdc.Sort();
}
#region Sorting
private bool isSorted;
private ListSortDirection sortDirection;
private PropertyDescriptor sortProperty;
protected override bool IsSortedCore
{
get { return isSorted; }
}
protected override bool SupportsSortingCore
{
get { return true; }
}
protected override ListSortDirection SortDirectionCore
{
get { return sortDirection; }
}
protected override PropertyDescriptor SortPropertyCore
{
get { return sortProperty; }
}
protected override void ApplySortCore(PropertyDescriptor property, ListSortDirection direction)
{
var items = Items as List<T>;
if (items != null)
{
var pc = new PropertyComparer<T>(property, direction);
items.Sort(pc);
isSorted = true;
}
else
{
isSorted = false;
}
sortProperty = property;
sortDirection = direction;
OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
}
protected override void RemoveSortCore()
{
isSorted = false;
OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
}
//public void Sort(PropertyDescriptor property, ListSortDirection direction)
//{
// ApplySortCore(property, direction);
//}
#endregion
#region Searching
protected override bool SupportsSearchingCore
{
get { return true; }
}
protected override int FindCore(PropertyDescriptor property, object key)
{
// Specify search columns
if (property == null) return -1;
// Get list to search
var items = Items as List<T>;
// Traverse list for value
if (items != null)
{
foreach (T item in items)
{
// Test column search value
string value = property.GetValue(item).ToString();
// If value is the search value, return the
// index of the data item
if (key.ToString() == value) return IndexOf(item);
}
}
return -1;
}
#endregion
#region IBindingListView 成员
public void ApplySort(ListSortDescriptionCollection sorts)
{
// Get list to sort
// Note: this.Items is a non-sortable ICollection<T>
var items = Items as List<T>;
// Apply and set the sort, if items to sort
if (items != null)
{
SortDescriptions = sorts;
comparers = new List<PropertyComparer<T>>();
foreach (ListSortDescription sort in sorts)
comparers.Add(new PropertyComparer<T>(sort.PropertyDescriptor, sort.SortDirection));
items.Sort(CompareValuesByProperties);
//_isSorted = true;
}
OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
}
public string Filter
{
get { return mFilterString; }
set
{
bool Include = false;
if (string.IsNullOrEmpty(mFilterString))
{
unfilteredItems.AddRange((ICollection)Items);
}
Clear();
foreach (T item in unfilteredItems)
{
if (mFilterHandler != null)
{
Include = true;
mFilterHandler.Invoke(item, ref Include);
if (Include) Add(item);
}
else
{
Add(item);
}
}
mFilterString = value;
}
}
public void RemoveFilter()
{
Clear();
foreach (T item in unfilteredItems)
{
Add(item);
}
unfilteredItems.Clear();
}
public ListSortDescriptionCollection SortDescriptions { get; set; }
public bool SupportsAdvancedSorting
{
get { return true; }
}
public bool SupportsFiltering
{
//get { return true; }
get { return false; }
}
#endregion
public FilterHandler FilterHandler
{
set
{
mFilterHandler = value;
if (mFilterHandler == null)
{
Filter = "";
}
else
{
Filter = "any no zero length string";
}
}
}
private int CompareValuesByProperties(T x, T y)
{
if (x == null)
return (y == null) ? 0 : -1;
if (y == null)
return 1;
foreach (var comparer in comparers)
{
int retval = comparer.Compare(x, y);
if (retval != 0)
return retval;
}
return 0;
}
#region ITypedList 成员
public PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors)
{
PropertyDescriptorCollection pdc;
if (null == listAccessors)
{
// Return properties in sort order.
pdc = properties;
}
else
{
// Return child list shape.
pdc = ListBindingHelper.GetListItemProperties(listAccessors[0].PropertyType);
}
return pdc;
}
// This method is only used in the design-time framework
// and by the obsolete DataGrid control.
public string GetListName(PropertyDescriptor[] listAccessors)
{
return typeof(T).Name;
}
#endregion
}
}
4.实现IEditableObject,INotifyPropertyChanged的测试实体类CategoryInfo
[Serializable]
public class CategoryInfo : BusinessObjectBase, IEditableObject
{
private int _categoryid;
private string _categoryname = string.Empty;
private DateTime dt;
private bool m_Editing; //是否处于编辑状态
private CategoryInfo originalCategoryInfo;
public int CategoryID
{
set
{
if (value != _categoryid)
{
_categoryid = value;
OnPropertyChanged("CategoryID");
}
}
get { return _categoryid; }
}
public string CategoryName
{
get { return _categoryname; }
set
{
if (value==null||string.IsNullOrEmpty(value.Trim()))
{
throw new ArgumentException("名称不能为空!");
}
if (value != _categoryname)
{
_categoryname = value;
OnPropertyChanged("CategoryName");
}
}
}
public DateTime Dt
{
get { return dt; }
set
{
if (value != dt)
{
dt = value;
OnPropertyChanged("Dt");
}
}
}
public static BindingListView<CategoryInfo> GetCategory()
{
var list = new BindingListView<CategoryInfo>
{
new CategoryInfo
{
CategoryID = 1,
CategoryName = "name1",
Dt = DateTime.Now,
},
new CategoryInfo
{
CategoryID = 2,
CategoryName = "name2",
Dt = DateTime.Now.AddDays(1),
},
};
return list;
}
#region IEditableObject 成员
public void BeginEdit()
{
if (!m_Editing)
{
originalCategoryInfo = new CategoryInfo
{
CategoryID = CategoryID,
CategoryName = CategoryName,
Dt = Dt,
};
m_Editing = true;
}
}
public void CancelEdit()
{
if (m_Editing)
{
CategoryID = originalCategoryInfo.CategoryID;
CategoryName = originalCategoryInfo.CategoryName;
Dt = originalCategoryInfo.Dt;
m_Editing = false;
}
}
public void EndEdit()
{
if (m_Editing)
{
originalCategoryInfo = new CategoryInfo();
m_Editing = false;
}
}
#endregion
}
5.测试窗体实现关键代码:
BindingListView<CategoryInfo> list=CategoryInfo.GetCategory();
datagridview1.DataSource=categoryInfoBindingSource;
dataGridView1.AutoGenerateColumns = true;
categoryInfoBindingSource.DataSource = list;
textBox1.DataBindings.Add("Text", categoryInfoBindingSource, "CategoryID", true);
textBox2.DataBindings.Add("Text", categoryInfoBindingSource, "CategoryName", true);
dateTimePicker1.DataBindings.Add("Value", categoryInfoBindingSource, "Dt", true);