由于将List<T>或者BindingList<T>绑定到BindingSource时,不支持BindingSource的排序和filter方法,特此扩展BindingListView<T>,再将BindingListView<T>绑定到BindingSource,可实现以上需求。
1.首先定义BusinessObjectBase类,实现INotifyPropertyChanged接口,可以做为实体类的基类。
public class 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>类,做为排序时的方法类。
public class PropertyComparer<T> : IComparer<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)
namespace 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);