Решил мини-статью написать, об этой гребаной пользовательской сортировке в гриде.
Может кто-то мимо граблей пролетит.
Пишу на примере DataSet-DataTable. При использовании Linq для SQL у меня получились те же яйца, вид сбоку. Воможно я его плохо знаю. Пусть тогда знающие люди поправят.
Во-первых, сортировать можно в двух местах: в DataGridView и в BindingSource.
Во-вторых, есть разница между сортировкой в DataGridView с привязанным источником (DataSource) и гридом в виртуальном режиме – DataGridView (DataGridView.VirtualMode = true).
Когда элемент управления DataGridView привязывается к внешнему источнику данных, необходимо использовать операции сортировки, предоставляемые этим источником данных.
BindingSource этой возможности не предоставляет.
Хотя мне непонятно почему, ведь у него есть метод Sort!
В виртуальном режиме и если источник поддерживает сортировку, то все отлично. Можно использовать оба конструктора:
• Sort(IComparer) Сортирует содержимое элемента управления DataGridView, используя реализацию интерфейса IComparer.
• Sort(DataGridViewColumn, ListSortDirection) Сортирует содержимое элемента управления DataGridView по убыванию или по возрастанию, основываясь на содержимом указанного столбца.
Даже больше. Можно не возиться с написанием IComparer, а использовать событие DataGridView.onSortCompare. Причем Net сердцем чует, имеет ли источник возможность сортировки. Если нет, то в это событие просто не игнорируется .
Если совсем тяжко, то можно в качестве источника указать List<T>, но тогда надо будет писать множество нужных методов и событий. Я не рискнул лезть в эти дебри.
Итак, мы потеряли все плюшки от того, что используем грид с привязанным источником данных, не поддерживающим сортировки. Но надо что-то делать!
Единственный путь – создание специального столбца для сортировки в режиме Programmatic, и отлов нажатия левой ноги мыши на ColumnHeaderMouseClick.
И тут варианты. Я описываю только возникавшие у меня потребности.
1. Сортировка по нескольким полям. Тут все просто. Забываем про DataGridView и делаем что-то вроде: BindungSource.Sort=”Field0 Asc, Field1 Desc”. И все! Простая строка решает проблему.
2. Сортировка по полям типа DataGridViewComboBox. Создаем в DataTablе вычисляемое поле. Делаем связь (Relation) между родителем-справочником и дочкой – нашей главной таблице. Если делать не вручную, то эта связь получить название вроде “ParenTable_ChildTable”.
В поле Expession созданного столбца пишем что-то вроде Parent(ParenTable_ChildTable).FieldName.
Теперь можно применить Sort(DataGridViewColumn, ListSortDirection). Только DataGridViewColumn должно быть по созданному столбцу.
3. Сортировка по функции. Если удастся использовать допустимые функции (
http://msdn.microsoft.com/ru-ru/library/system.data.datacolumn.expression(v=vs.110).aspx ), то делаем по пункту 2. Например, по произведению двух полей - Количество*Цена.
Если нет, то есть два выхода, как минимум.
Для примера, задача. Сделать «истинную» сортировку IP-адресов. Что бы 10.2.0.0 шло раньше 10.100.0.0
Первый – возвратить из базы дополнительный столбец, с предрассчитанным и удобными для сортировки значениями. Для IP это будет примерно так: 010.002.000.000.
Второй – рассчитать этот столбец после загрузки. Какой способ выбрать – думать самому в разрезе производительности среднего клиентского компа, скорости сети и других параметров.
Не забыть сделать DataTable.AccetpChanges до того, как заработает обработчик RowChanged!
Оба этих способа не очень хороши, так как при изменении исходного поля надо вручную менять значение в «поле для сортировки»