實現排序,INotifyPropertyChanged,filter的BindingListView

由於將List或者BindingList綁定到BindingSource時,不支持BindingSource的排序和filter方法,特此擴展BindingListView,再將BindingListView綁定到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類,做爲排序時的方法類。

public class PropertyComparer<T> : IComparer<T>
    {
        private ListSortDirection _direction;
        private PropertyDescriptor _property;

        public PropertyComparer(PropertyDescriptor property, ListSortDirection direction)
        {
            _property = property;
            _direction = direction;
        }

        /// 
        /// 比較方法
        /// 
        /// 相對屬性x
        /// 相對屬性y
        /// 
        #region IComparer

        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類,實現排序和過濾(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
            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 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);

發佈了34 篇原創文章 · 獲贊 3 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章