Сортировка пользовательских столбцов в DataGridView, привязанных к BindingList - PullRequest
1 голос
/ 16 мая 2011

У меня есть DataGridView, который связан с данными BindingList. Мой DataGridView также имеет несколько пользовательских столбцов, которые я добавил. Они не привязаны к данным, а генерируются на основе элементов в моем BindingList (то есть: элемент в моем BindingList типа A имеет свойство типа B; в моем пользовательском столбце отображается B.Name ( EDIT). : В этом случае «Имя» является свойством класса B, и, таким образом, свойство, представленное столбцом, не находится непосредственно в элементах в BindingList)).

Мне нужно иметь возможность сортировать все столбцы в моем DataGridView. DataGridView имеет два метода сортировки: Sort (IComparer) и Sort (DataGridViewColumn, ListSortDirection). Я использую второй для сортировки столбцов с привязкой к данным, но он, конечно, выдает исключение при использовании столбца без привязки к данным. Первый метод вызовет исключение, если DataSource не равен NULL.

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

EDIT:

В данный момент я обрабатываю щелчок по заголовку столбца, следуя приведенным здесь инструкциям: http://msdn.microsoft.com/en-us/library/system.windows.forms.datagridview.columnheadermouseclick.aspx

Проблема возникает на линии:

dataGridView1.Sort(newColumn, direction);

Все отлично работает, когда newColumn содержит одно из свойств объекта в моем BindingList. Но чтобы отсортировать один из моих пользовательских столбцов, мне нужно будет вообще избежать этой строки и найти другой способ сортировки сетки данных на основе этого столбца. Означает ли это необходимость создания моей собственной функции сортировки? Кажется, это может быть огромная боль.

1 Ответ

1 голос
/ 16 мая 2011

ЗАКЛЮЧИТЕЛЬНОЕ Редактирование / Ответ: Я не могу придумать, как это сделать, все еще используя механизм сортировки, встроенный в DataGridView.Если бы я был на вашем месте, я бы, вероятно, просто изменил SortMode каждого столбца на «Programmatic», а затем обработал бы «ColumnHeaderMouseClick» самостоятельно.В этот момент вы должны вызвать метод сортировки в вашем модифицированном классе BindingList, который будет выполнять сортировку по мере необходимости в зависимости от того, по какому столбцу был нажатЭто позволяет избежать использования метода сортировки DGV и сортирует базовый список напрямую.

Полный дискурс Расположен в разделе комментариев.Первоначальный ответ следует немедленно:

РЕДАКТИРОВАТЬ: Из-за некоторой путаницы в отношении проблемы и последующего обсуждения ее, у меня есть новое предложение в комментариях к этому ответу.Я оставляю оригинальный ответ, который я разместил, чтобы мы могли сослаться на него.

Мне недавно пришлось это сделать - и я не буду лгать, это была настоящая боль.Я пришел к решению (с помощью некоторых друзей здесь, в SO), так что здесь идет.Я создал новый интерфейс на основе IComparer, который позволяет указывать как столбец, так и направление.Я сделал это только потому, что мне нужно, чтобы мой код сортировки был как можно более универсальным - у меня есть ДВА сетки, которые нужно сортировать таким образом, и я не хочу поддерживать код дважды.Вот интерфейс, довольно простой:

   public interface IByColumnComparer : IComparer
   {
      string SortColumn { get; set; }
      bool SortDescending { get; set; }
   }

Очевидно, что если вы не беспокоитесь о том, чтобы сохранить что-то общее (вам, вероятно, следует), то это не является строго обязательным.Затем я создал новый класс, основанный на BindingList <>.Это позволило мне переопределить код сортировки и предоставить свой собственный IByColumnComparer для столбцов за столбцами , что позволило добиться необходимой гибкости.Проверьте это:

public class SortableGenericCollection<T> : BindingList<T>
{
  IByColumnComparer GenericComparer = null; 

  public SortableGenericCollection(IByColumnComparer SortingComparer)
  {
     GenericComparer = SortingComparer;
  }


  protected override bool SupportsSortingCore
  {
     get
     {
        return true;
     }
  }

  protected override bool IsSortedCore
  {
     get
     {
        for (int i = 0; i < Items.Count - 1; ++i)
        {
           T lhs = Items[i];
           T rhs = Items[i + 1];
           PropertyDescriptor property = SortPropertyCore;
           if (property != null)
           {
              object lhsValue = lhs == null ? null :
              property.GetValue(lhs);
              object rhsValue = rhs == null ? null :
              property.GetValue(rhs);
              int result;
              if (lhsValue == null)
              {
                 result = -1;
              }
              else if (rhsValue == null)
              {
                 result = 1;
              }
              else
              {
                 result = GenericComparer.Compare(lhs, rhs); 
              }
              if (result >= 0)
              {
                 return false;
              }
           }
        }
        return true;
     }
  }

  private ListSortDirection sortDirection;
  protected override ListSortDirection SortDirectionCore
  {
     get
     {
        return sortDirection;
     }
  }

  private PropertyDescriptor sortProperty;
  protected override PropertyDescriptor SortPropertyCore
  {
     get
     {
        return sortProperty;
     }
  }

  protected override void ApplySortCore(PropertyDescriptor prop,
  ListSortDirection direction)
  {
     sortProperty = prop;
     sortDirection = direction;

     GenericComparer.SortColumn = prop.Name;
     GenericComparer.SortDescending = direction == ListSortDirection.Descending ? true : false;

     List<T> list = (List<T>)Items;
     list.Sort(delegate(T lhs, T rhs)
     {
        if (sortProperty != null)
        {
           object lhsValue = lhs == null ? null :
           sortProperty.GetValue(lhs);
           object rhsValue = rhs == null ? null :
           sortProperty.GetValue(rhs);
           int result;
           if (lhsValue == null)
           {
              result = -1;
           }
           else if (rhsValue == null)
           {
              result = 1;
           }
           else
           {
              result = GenericComparer.Compare(lhs, rhs);
           }
           return result;
        }
        else
        {
           return 0;
        }
     });
  }

  protected override void RemoveSortCore()
  {
     sortDirection = ListSortDirection.Ascending;
     sortProperty = null;
  }
}

Теперь, как вы можете видеть в методе ApplySortCore, я получаю столбец и направление непосредственно из DataGridView - это означает, что я не вызываю это программно.Это не похоже на то, что вы хотите сделать, но вы можете легко изменить этот код, если вам нужно вызвать его программно и передать соответствующий IByColumnComparer.Я хочу показать вам все это, чтобы вы могли понять, как изменить алгоритм сортировки, что весьма полезно.

Особая благодарность @MartinhoFernandes за предложения относительно того, как сделать этот класс более универсальным.

...