Сортировка DataGridView и, например, BindingList in.NET

Я использую BindingList<T> в моих Windows Forms, который содержит список"IComparable<Contact>" контакт-объекты. Теперь я хотел бы, чтобы пользователь мог Сортировать по любому столбцу, отображаемому в сетке.

существует способ, описанный в MSDN online, который показывает, как реализовать пользовательскую коллекцию на основе BindingList<T>, которая позволяет сортировать. Но нет ли Sort-event или чего-то, что может быть поймано в DataGridView (или, еще лучше, на BindingSource) для сортировки базовой коллекции с помощью custom код?

мне не очень нравится способ, описанный в MSDN. С другой стороны, я мог бы легко применить запрос LINQ к коллекции.

6 ответов


Я очень ценю Маттиас' для своей простоты и красоты.

, а это дает отличные результаты для низких объемов данных, при работе с большими объемами данных производительность не так хорошо, из-за отражения.

Я провел тест с коллекцией простых объектов данных, насчитывающих 100000 элементов. Сортировка по свойству целочисленного типа заняла около 1 мин. Реализации я собираюсь более подробно следует ~200мс.

основная идея заключается в пользу строгой типизацией сравнения, сохраняя при этом способ ApplySortCore универсальный. Следующее заменяет универсальный делегат сравнения вызовом конкретного компаратора, реализованного в производном классе:

новое в SortableBindingList:

protected abstract Comparison<T> GetComparer(PropertyDescriptor prop);

ApplySortCore изменения в:

protected override void ApplySortCore(PropertyDescriptor prop, ListSortDirection direction)
{
    List<T> itemsList = (List<T>)this.Items;
    if (prop.PropertyType.GetInterface("IComparable") != null)
    {
        Comparison<T> comparer = GetComparer(prop);
        itemsList.Sort(comparer);
        if (direction == ListSortDirection.Descending)
        {
            itemsList.Reverse();
        }
    }

    isSortedValue = true;
    sortPropertyValue = prop;
    sortDirectionValue = direction;
}

Теперь в производном классе нужно реализовать компараторы для каждого сортируемого свойства:

class MyBindingList:SortableBindingList<DataObject>
{
        protected override Comparison<DataObject> GetComparer(PropertyDescriptor prop)
        {
            Comparison<DataObject> comparer;
            switch (prop.Name)
            {
                case "MyIntProperty":
                    comparer = new Comparison<DataObject>(delegate(DataObject x, DataObject y)
                        {
                            if (x != null)
                                if (y != null)
                                    return (x.MyIntProperty.CompareTo(y.MyIntProperty));
                                else
                                    return 1;
                            else if (y != null)
                                return -1;
                            else
                                return 0;
                        });
                    break;

                    // Implement comparers for other sortable properties here.
            }
            return comparer;
        }
    }
}

этот вариант требует немного больше кода, но, если производительность является проблемой, я думаю, что это стоит усилий.


я погуглил и попробовал еще раз...

в .NET пока нет встроенного способа. Вы должны реализовать пользовательский класс на основе BindingList<T>. Один из способов описан в привязка пользовательских данных, Часть 2 (MSDN). Я, наконец, производит другую реализацию ApplySortCore - метод для обеспечения реализации, которая не зависит от проекта.

protected override void ApplySortCore(PropertyDescriptor property, ListSortDirection direction)
{
    List<T> itemsList = (List<T>)this.Items;
    if(property.PropertyType.GetInterface("IComparable") != null)
    {
        itemsList.Sort(new Comparison<T>(delegate(T x, T y)
        {
            // Compare x to y if x is not null. If x is, but y isn't, we compare y
            // to x and reverse the result. If both are null, they're equal.
            if(property.GetValue(x) != null)
                return ((IComparable)property.GetValue(x)).CompareTo(property.GetValue(y)) * (direction == ListSortDirection.Descending ? -1 : 1);
            else if(property.GetValue(y) != null)
                return ((IComparable)property.GetValue(y)).CompareTo(property.GetValue(x)) * (direction == ListSortDirection.Descending ? 1 : -1);
            else
                return 0;
        }));
    }

    isSorted = true;
    sortProperty = property;
    sortDirection = direction;
}

используя этот, вы можете сортировать по любому члену, который реализует IComparable.


Я понимаю, что все эти ответы были хороши в то время, когда они были написаны. Возможно, так оно и есть. Я искал что-то похожее и нашел альтернативное решение для преобразования любой список или коллекция для сортировки BindingList<T>.

вот важный фрагмент (ссылка на полный образец приведена ниже):

void Main()
{
    DataGridView dgv = new DataGridView();
    dgv.DataSource = new ObservableCollection<Person>(Person.GetAll()).ToBindingList();
}    

это решение использует метод расширения существующих в Entity Framework библиотека. Поэтому, пожалуйста, рассмотрите следующее прежде чем продолжить:

  1. если вы не хотите использовать Entity Framework, это нормально, это решение также не использует его. Мы просто используем метод расширения, который они разработали. Размер EntityFramework.dll составляет 5 Мб. Если он слишком велик для вас в эпоху петабайт, не стесняйтесь извлекать метод и его зависимости из приведенной выше ссылки.
  2. если вы используете (или хотите использовать) Entity Framework (>=v6.0), вам не о чем беспокоиться. Просто установите Entity Framework NuGet-пакет и вперед.

Я загрузил помощью linqpad пример кода здесь.

  1. загрузите образец, откройте его с помощью LINQPad и нажмите F4.
  2. вы должны увидеть EntityFramework.dll в красном цвете. Загрузите dll из этого расположение. Просмотрите и добавьте ссылку.
  3. нажмите OK. Нажмите Клавишу F5.

Как вы можете видеть, вы можете сортировка по всем четырем столбцам различных типов данных, щелкнув их заголовки столбцов в элементе управления DataGridView.

те, у кого нет LINQPad, все еще могут загрузить запрос и открыть его с помощью блокнота, чтобы увидеть полный образец.


вот альтернатива, которая очень чистая и отлично работает в моем случае. У меня уже были определенные функции сравнения, настроенные для использования со списком.Сортировка (сравнение), поэтому я просто адаптировал это из частей других примеров StackOverflow.

class SortableBindingList<T> : BindingList<T>
{
 public SortableBindingList(IList<T> list) : base(list) { }

 public void Sort() { sort(null, null); }
 public void Sort(IComparer<T> p_Comparer) { sort(p_Comparer, null); }
 public void Sort(Comparison<T> p_Comparison) { sort(null, p_Comparison); }

 private void sort(IComparer<T> p_Comparer, Comparison<T> p_Comparison)
 {
  if(typeof(T).GetInterface(typeof(IComparable).Name) != null)
  {
   bool originalValue = this.RaiseListChangedEvents;
   this.RaiseListChangedEvents = false;
   try
   {
    List<T> items = (List<T>)this.Items;
    if(p_Comparison != null) items.Sort(p_Comparison);
    else items.Sort(p_Comparer);
   }
   finally
   {
    this.RaiseListChangedEvents = originalValue;
   }
  }
 }
}

вот это новое реализовать, используя несколько новых трюков.

базовый тип IList<T> необходимо реализовать void Sort(Comparison<T>) или вы должны передать делегат, чтобы вызвать функцию сортировки для вас. (IList<T> нет


Не для пользовательских объектов. В .Net 2.0 мне пришлось свернуть сортировку с помощью BindingList. В .Net 3.5 может быть что-то новое, но я еще не изучил это. Теперь, когда есть LINQ и параметры сортировки, которые поставляются, если это теперь может быть проще реализовать.