среда, 1 июня 2011 г.

Сортировка в DataGridView

  Дело было давно, но возможно не потеряло своей актуальности.
При разработке одного из приложений (WinForms) нужно было организовать сортировку в столбцах DataGridView. Чего было только не перепробовано, но результата не было.
 После долгих поисков в Интернете я все же нашел выход.



В блоге одного программиста (к сожалению, не помню ни имени ни адреса) я нашел направление для дальнейшего копания. В блоге был предоставлен и код, так что не все в этом посте моя заслуга.
  А направление вот какое. Нужно было организовать свой BindingList<T> с сортировкой. К сожалению, исходного кода, который был в том блоге, у меня не сохранилось. Так что привожу уже свой, доработанный.

  1.   /// <summary>
  2.   /// Provides a generic collection that supports data binding and additionally supports sorting.
  3.   /// See http://msdn.microsoft.com/en-us/library/ms993236.aspx
  4.   /// If the elements are IComparable it uses that; otherwise compares the ToString()
  5.   /// </summary>
  6.   /// <typeparam name="T">The type of elements in the list.</typeparam>
  7.   [Serializable]
  8.   public class SortableBindingList<T> : BindingList<T>
  9.   {
  10.     private bool _isSorted;
  11.     private ListSortDirection _sortDirection = ListSortDirection.Ascending;
  12.     private PropertyDescriptor _sortProperty;
  13.  
  14.     /// <summary>
  15.     /// Gets a value indicating whether the list supports sorting.
  16.     /// </summary>
  17.     protected override bool SupportsSortingCore
  18.     {
  19.       get { return true; }
  20.     }
  21.  
  22.     /// <summary>
  23.     /// Gets a value indicating whether the list is sorted.
  24.     /// </summary>
  25.     protected override bool IsSortedCore
  26.     {
  27.       get { return _isSorted; }
  28.     }
  29.  
  30.     /// <summary>
  31.     /// Gets the direction the list is sorted.
  32.     /// </summary>
  33.     protected override ListSortDirection SortDirectionCore
  34.     {
  35.       get { return _sortDirection; }
  36.     }
  37.  
  38.     /// <summary>
  39.     /// Gets the property descriptor that is used for sorting the list if sorting is implemented in a derived class; otherwise, returns null
  40.     /// </summary>
  41.     protected override PropertyDescriptor SortPropertyCore
  42.     {
  43.       get { return _sortProperty; }
  44.     }
  45.  
  46.     /// <summary>
  47.     /// Removes any sort applied with ApplySortCore if sorting is implemented
  48.     /// </summary>
  49.     protected override void RemoveSortCore()
  50.     {
  51.       _sortDirection = ListSortDirection.Ascending;
  52.       _sortProperty = null;
  53.     }
  54.  
  55.     /// <summary>
  56.     /// Sorts the items if overridden in a derived class
  57.     /// </summary>
  58.     /// <param name="prop"></param>
  59.     /// <param name="direction"></param>
  60.     protected override void ApplySortCore(PropertyDescriptor prop, ListSortDirection direction)
  61.     {
  62.       _sortProperty = prop;
  63.       _sortDirection = direction;
  64.  
  65.       if (_sortProperty == null) return; //nothing to sort on
  66.       List<T> list = Items as List<T>;
  67.       if (list == null) return;
  68.  
  69.       list.Sort(delegate(T lhs, T rhs)
  70.       {
  71.  
  72.         object lhsValue = lhs == null ? null : _sortProperty.GetValue(lhs);
  73.         object rhsValue = rhs == null ? null : _sortProperty.GetValue(rhs);
  74.         int result = 0;
  75.         if (lhsValue == null && rhsValue == null)
  76.         {
  77.           result = 0;
  78.         }
  79.         else if (lhsValue == null)
  80.         {
  81.           result = -1;
  82.         }
  83.         else if (rhsValue == null)
  84.         {
  85.           result = 1;
  86.         }
  87.         else
  88.         {
  89.           if (lhsValue is IComparable)
  90.           {
  91.             result = ((IComparable)lhsValue).CompareTo(rhsValue);
  92.           }
  93.           else if (!lhsValue.Equals(rhsValue))//not comparable, compare ToString
  94.           {
  95.             result = lhsValue.ToString().CompareTo(rhsValue.ToString());
  96.           }
  97.         }
  98.         if (_sortDirection == ListSortDirection.Descending)
  99.           result = -result;
  100.         return result;
  101.       });
  102.  
  103.       _isSorted = true;
  104.       //fire an event that the list has been changed.
  105.       OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
  106.     }
  107.  
  108.     public static implicit operator List<T>(SortableBindingList<T> sortableBindingList)
  109.     {
  110.       return sortableBindingList.Items.ToList();
  111.     }
  112.   }
* This source code was highlighted with Source Code Highlighter.
   В исходном коде я поменял на самом деле не так много.
   Была ошибка со сравнением. То есть, когда список проводил сравнение своих элементов, то не учитывал, что они могут быть и равны (да, да). Кроме этого я добавил Serializable атрибут. Было и еще что-то, чего я уж не упомню.
   Пользоваться этим классом просто. Приведу просто пример, из которого все станет ясно:

  1.       var list = new SortableBindingList<FinanceDocument>
  2.       {
  3.         new FinanceDocument { Number="1D", Date=DateTime.Now },
  4.         new FinanceDocument { Number="3D", Date=DateTime.Now.AddDays(-1) },
  5.         new FinanceDocument { Number="1G", Date=DateTime.Now.AddDays(-4) },
  6.         new FinanceDocument { Number="1H", Date=DateTime.Now },
  7.         new FinanceDocument { Number="4D", Date=DateTime.Now }
  8.       };
  9.       dataGridView.DataSource = new BindingSource { DataSource = list };
* This source code was highlighted with Source Code Highlighter.
   Надеюсь, кому-то этот пост сократить время на поиски решения. А может кто-то предложит и другой способ.

2 комментария: