DataGridView - Sort Generic Lists by Click on Column Headers

前端 未结 1 694
闹比i
闹比i 2020-12-07 03:32

When I assign a List to DataSource of a DataGridView, when I click on column headers, nothing happens and sorting doesn

相关标签:
1条回答
  • 2020-12-07 04:06

    How Sorting works in a data-bound DataGridView

    When you click on a column header in a data-bound DataGridView which its automatic sorting is enabled, first it checks if the list behind of the DataSource property is IBindingList, then using SupportsSorting checks if the list supports sorting. Then it calls ApplySort method to sort the list.

    When you use a DataTable as data source of the grid, the list behind the data source is actually a DataView which implements IBindingList that supports sorting.

    To have automatic support for sorting in a DataGridView, the list should implement IBindingList and its members which are related to sort.

    Enable sorting in a BindingList<T>

    To have typed list implementation of IBindingList which also supports sorting, a good option is deriving from BindingList<T>. It implements IBindingList but it doesn't support sorting by default. You can override it's methods and properties which are related to sorting: SupportsSortingCore, IsSortedCore, SortPropertyCore, SortDirectionCore and ApplySortCore.

    Existing Implementations

    There are some implementations around:

    • SortableBindingList<T> implementation which is used in Entity Framework.

    • SortableSearchableList<T> which is published in an MSDN article

    • If you are using Entity Framework, ToBindingList method of the Local property of DbSet<T> returns a sortable BindingList<T>.

    SortableBindingList

    Here is an implementation which is borrowed from Microsoft internal implementations with some small changes:

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Diagnostics;
    using System.Linq;
    using System.Reflection;
    using System.Xml.Linq;
    
    public class SortableBindingList<T> : BindingList<T>
    {
        private bool _isSorted;
        private ListSortDirection _sortDirection;
        private PropertyDescriptor _sortProperty;
    
        public SortableBindingList(List<T> list)
            : base(list)
        {
        }
        protected override void ApplySortCore(PropertyDescriptor prop, ListSortDirection direction)
        {
            if (PropertyComparer.CanSort(prop.PropertyType))
            {
                ((List<T>)Items).Sort(new PropertyComparer(prop, direction));
                _sortDirection = direction;
                _sortProperty = prop;
                _isSorted = true;
                OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
            }
        }
        protected override void RemoveSortCore()
        {
            _isSorted = false;
            _sortProperty = null;
        }
    
        protected override bool IsSortedCore
        {
            get { return _isSorted; }
        }
    
        protected override ListSortDirection SortDirectionCore
        {
            get { return _sortDirection; }
        }
    
        protected override PropertyDescriptor SortPropertyCore
        {
            get { return _sortProperty; }
        }
    
        protected override bool SupportsSortingCore
        {
            get { return true; }
        }
    
        internal class PropertyComparer : Comparer<T>
        {
            private readonly IComparer _comparer;
            private readonly ListSortDirection _direction;
            private readonly PropertyDescriptor _prop;
            private readonly bool _useToString;
    
            public PropertyComparer(PropertyDescriptor prop, ListSortDirection direction)
            {
                if (!prop.ComponentType.IsAssignableFrom(typeof(T)))
                {
                    throw new MissingMemberException(typeof(T).Name, prop.Name);
                }
    
                Debug.Assert(CanSort(prop.PropertyType), "Cannot use PropertyComparer unless it can be compared by IComparable or ToString");
    
                _prop = prop;
                _direction = direction;
    
                if (CanSortWithIComparable(prop.PropertyType))
                {
                    var property = typeof(Comparer<>).MakeGenericType(new[] { prop.PropertyType }).GetTypeInfo().GetDeclaredProperty("Default");
                    _comparer = (IComparer)property.GetValue(null, null);
                    _useToString = false;
                }
                else
                {
                    Debug.Assert(
                        CanSortWithToString(prop.PropertyType),
                        "Cannot use PropertyComparer unless it can be compared by IComparable or ToString");
    
                    _comparer = StringComparer.CurrentCultureIgnoreCase;
                    _useToString = true;
                }
            }
    
            public override int Compare(T left, T right)
            {
                var leftValue = _prop.GetValue(left);
                var rightValue = _prop.GetValue(right);
    
                if (_useToString)
                {
                    leftValue = leftValue != null ? leftValue.ToString() : null;
                    rightValue = rightValue != null ? rightValue.ToString() : null;
                }
    
                return _direction == ListSortDirection.Ascending
                           ? _comparer.Compare(leftValue, rightValue)
                           : _comparer.Compare(rightValue, leftValue);
            }
    
            public static bool CanSort(Type type)
            {
                return CanSortWithToString(type) || CanSortWithIComparable(type);
            }
    
            private static bool CanSortWithIComparable(Type type)
            {
                return type.GetInterface("IComparable") != null ||
                       (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>));
            }
    
            private static bool CanSortWithToString(Type type)
            {
                return type.Equals(typeof(XNode)) || type.IsSubclassOf(typeof(XNode));
            }
        }
    }
    
    public static class EnumerableExtensions
    {
        public static BindingList<T> ToSortableBindingList<T>(this IEnumerable<T> source)
        {
            return new SortableBindingList<T>(source.ToList());
        }
    }
    

    Example

    private void Form1_Load(object sender, EventArgs e)
    {
        var list = Enumerable.Range(1, 10)
            .Select(x => new { A = x, B = $"x" })
            .ToSortableBindingList();
        dataGridView1.DataSource = list;
    }
    
    0 讨论(0)
提交回复
热议问题