Сортировать представление DataGridView по нескольким столбцам?
Я искал пример сортировки DataGridView по нескольким столбцам, но, похоже, не смог найти пример, который делает то, что я хотел бы.
В принципе, у меня есть привязанный элемент управления DataGridView (привязанный к DataTable/DataView), и привязанный DataTable имеет два столбца:- priority и date. Я хотел бы отсортировать по дате в приоритетном порядке. То есть приоритетный столбец принимает precendence, затем его дату, но оба могут быть восходящими или нисходящими.
Так, например, я могу имейте низкий приоритет, ранняя дата первая (порядок по приоритету asc, дата asc) , и, щелкнув заголовок столбца дата, переключитесь на низкий приоритет, поздняя дата первая (порядок по приоритету asc, дата desc) . Если я затем нажму на приоритет, я хотел бы сначала иметь высокий приоритет, затем позднюю дату (текущий порядок сортировки для столбца даты- order by priority desc, date desc) , но затем иметь возможность щелкнуть заголовок столбца даты, чтобы переключиться на высокий приоритет, раннюю дату (order by приоритет desc, дата asc) .
В идеале, я хотел бы отсортировать глифы на обоих столбцах, чтобы показать восходящий или нисходящий.
Любые идеи или указания будут приняты с благодарностью.
Это (см. ниже), кажется, довольно близко, но глифы не работают правильно.
using System;
using System.Windows.Forms;
namespace WindowsFormsApplication4
{
public partial class Form1 : Form
{
DataSet1 dataset;
public Form1()
{
InitializeComponent();
dataset = new DataSet1(); // two columns: Priority(Int32) and date (DateTime)
dataset.DataTable1.AddDataTable1Row(1, DateTime.Parse("01-jan-10"));
dataset.DataTable1.AddDataTable1Row(1, DateTime.Parse("02-jan-10"));
dataset.DataTable1.AddDataTable1Row(1, DateTime.Parse("03-jan-10"));
dataset.DataTable1.AddDataTable1Row(2, DateTime.Parse("04-jan-10"));
dataset.DataTable1.AddDataTable1Row(2, DateTime.Parse("05-jan-10"));
dataset.DataTable1.AddDataTable1Row(2, DateTime.Parse("06-jan-10"));
dataset.DataTable1.AddDataTable1Row(3, DateTime.Parse("07-jan-10"));
dataset.DataTable1.AddDataTable1Row(3, DateTime.Parse("08-jan-10"));
dataset.DataTable1.AddDataTable1Row(3, DateTime.Parse("09-jan-10"));
dataGridView1.DataSource = dataset.DataTable1.DefaultView;
dataGridView1.AllowUserToAddRows = false;
dataGridView1.Columns[0].SortMode = DataGridViewColumnSortMode.Programmatic;
dataGridView1.Columns[1].SortMode = DataGridViewColumnSortMode.Programmatic;
dataGridView1.Columns[0].HeaderCell.SortGlyphDirection = SortOrder.Ascending;
dataGridView1.Columns[1].HeaderCell.SortGlyphDirection = SortOrder.Ascending;
}
private void dataGridView1_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)
{
DataGridViewColumn[] column = new[] { dataGridView1.Columns[0], dataGridView1.Columns[1] };
DataGridViewColumnHeaderCell headerCell = dataGridView1.Columns[e.ColumnIndex].HeaderCell;
if (headerCell.SortGlyphDirection != SortOrder.Ascending)
headerCell.SortGlyphDirection = SortOrder.Ascending;
else
headerCell.SortGlyphDirection = SortOrder.Descending;
String sort = column[0].DataPropertyName + " " + fnSortDirection(column[0])
+ ", "
+ column[1].DataPropertyName + " " + fnSortDirection(column[1]);
dataset.DataTable1.DefaultView.Sort = sort;
this.textBox1.Text = sort;
}
private String fnSortDirection(DataGridViewColumn column)
{
return column.HeaderCell.SortGlyphDirection != SortOrder.Descending ? "asc" : "desc";
}
}
}
4 ответов:
В первый раз, когда я прочитал это, я полностью пропустил часть о сортировке по нескольким столбцам одновременно (моя вина, а не ваша; вопрос был совершенно ясен).
Если это так, то вам придется написать код, который будет обрабатывать это самостоятельно. Предоставленный элемент управления
DataGridViewпо умолчанию не поддерживает многоколоночную сортировку. К счастью, другие уже проделали большую работу, чтобы реализовать это для вас. Вот некоторые из них: образцы:
- DataGridView Многоколоночная сортировка (CodeProject)
- Как разрешить сортировку по нескольким столбцам в пользовательской привязке данных (CodeProject)
Альтернативно, если вы привязываете свой
DataGridViewк источнику данных, этот источник данных может быть отсортирован по нескольким столбцам, и элемент управленияDataGridViewбудет уважать эту сортировку. Любой источник данных, реализующийIBindingListViewи предоставляющий СВОЙСТВОSort, будет работать для многоколоночной сортировки.
Однако, независимо от маршрута, который вы выбрали для включения сортировки по нескольким столбцам, вы не добьетесь большого успеха в принудительном отображении символаDataGridViewдля отображения глифа стрелки сортировки на нескольких столбцах. Самым простым решением здесь является пользовательское рисование только заголовков столбцов, чтобы обеспечить свой собственный глиф сортировки.Для этого прикрепите обработчик к
DataGridView.CellPaintingсобытие и проверка на наличиеRowIndexзначения -1 (указывает на заголовок столбца). Здесь есть полный пример заголовков столбцов, нарисованных владельцем . Я сильно рекомендуем придерживаться обычного значка со стрелкой, но как только вы пройдете этот маршрут, возможности действительно неограниченны. Вы можете сделать заголовки столбцов похожими на все, что хотите, и даже указать относительный вес каждого столбца в порядке сортировки с помощью различных значков.Вы также можете выбрать, чтобы получить новый класс из
DataGridViewColumnHeaderCellи переопределить егоPaintметод . Это, вероятно, более чистый, более объектно-ориентированный способ достижения того же самого.
Когда DataGridView привязывается к источнику данных (DataView, BindingSource, Table, DataSet+ "tablename" ) во всех случаях он ссылается на DataView. Получить ссылку на этот DataView и установить Sort (и Filter), Как вы хотите:
DataView dv = null; CurrencyManager cm = (CurrencyManager)(dgv.BindingContext[dgv.DataSource, dgv.DataMember]); if (cm.List is BindingSource) { // In case of BindingSource it may be chain of BindingSources+relations BindingSource bs = (BindingSource)cm.List; while (bs.List is BindingSource) { bs = bs.List as BindingSource; } if (bs.List is DataView) { dv = bs.List as DataView; } } else if (cm.List is DataView) { // dgv bind to the DataView, Table or DataSet+"tablename" dv = cm.List as DataView; } if (dv != null) { dv.Sort = "somedate desc, firstname"; // dv.Filter = "lastname = 'Smith' OR lastname = 'Doe'"; // You can Set the Glyphs something like this: int somedateColIdx = 5; // somedate int firstnameColIdx = 3; // firstname dgv.Columns[somedateColIdx].HeaderCell.SortGlyphDirection = SortOrder.Descending; dgv.Columns[firstnameColIdx].HeaderCell.SortGlyphDirection = SortOrder.Ascending; }Примечание: имена столбцов, используемые в сортировке и фильтре, соответствуют именам столбцов в DataTable , Имена столбцов в DataGridView-это базовые имена свойств данных, используемые для привязки (имена свойств для классы, имена столбцов для таблиц данных и т. д.). Имя столбца, используемого в DataView, можно получить следующим образом:
string colName = dgv.Columns[colIdx].DataPropertyNameВ зависимости от того, как вы хотите отслеживать сортированные столбцы (colSequence, colName, asc/desc, dgvColIdx), вы можете решить, как построить выражение сортировки и фильтрации и установить SortGlyph в dgv (я сделал hardcode для простоты).
Хорошо.
Следуя предложениям Коди выше, у меня теперь есть кое-что, что, кажется, работает так, как ожидалось. Я подклассировал HeaderCell и перегнал метод Paint (но обманул, установив SortGlyphDirection непосредственно перед базой.Paint) и DGV теперь рисует несколько сортировочных глифов.using System; using System.Collections.Generic; using System.Linq; using System.Windows.Forms; namespace WindowsFormsApplication4 { public partial class Form1 : Form { DataSet1 dataset; public Form1() { InitializeComponent(); dataset = new DataSet1(); // three columns: Priority(Int32), Date (DateTime) and Description(String) dataset.DataTable1.AddDataTable1Row(1, DateTime.Parse("01-jan-10"), "this"); dataset.DataTable1.AddDataTable1Row(1, DateTime.Parse("02-jan-10"), "is"); dataset.DataTable1.AddDataTable1Row(1, DateTime.Parse("03-jan-10"), "a"); dataset.DataTable1.AddDataTable1Row(2, DateTime.Parse("04-jan-10"), "sample"); dataset.DataTable1.AddDataTable1Row(2, DateTime.Parse("05-jan-10"), "of"); dataset.DataTable1.AddDataTable1Row(2, DateTime.Parse("06-jan-10"), "the"); dataset.DataTable1.AddDataTable1Row(3, DateTime.Parse("07-jan-10"), "data"); dataset.DataTable1.AddDataTable1Row(3, DateTime.Parse("08-jan-10"), "in"); dataset.DataTable1.AddDataTable1Row(3, DateTime.Parse("09-jan-10"), "use"); dataGridView1.DataSource = dataset.DataTable1.DefaultView; dataGridView1.AllowUserToAddRows = false; dataGridView1.Columns[0].HeaderCell = new MyDataGridViewColumnHeaderCell(); dataGridView1.Columns[1].HeaderCell = new MyDataGridViewColumnHeaderCell(); dataGridView1.Columns[0].SortMode = DataGridViewColumnSortMode.Programmatic; dataGridView1.Columns[1].SortMode = DataGridViewColumnSortMode.Programmatic; } private void dataGridView1_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e) { DataGridViewColumn clickedColumn = dataGridView1.Columns[e.ColumnIndex]; if (clickedColumn.HeaderCell is MyDataGridViewColumnHeaderCell) { DoMultiColumnSort(); } else { dataGridView1.Columns.OfType<DataGridViewColumn>() .Where(column => column.HeaderCell is MyDataGridViewColumnHeaderCell) .ForEach(column => ((MyDataGridViewColumnHeaderCell)column.HeaderCell).SortOrderDirection = SortOrder.None); } this.textBox1.Text = dataset.DataTable1.DefaultView.Sort; } private void DoMultiColumnSort() { var sortClauses = dataGridView1.Columns.OfType<DataGridViewColumn>() .Where(column => column.HeaderCell is MyDataGridViewColumnHeaderCell) .Select(column => GetSortClause(column)); dataset.DataTable1.DefaultView.Sort = String.Join(",", sortClauses); } private String GetSortClause(DataGridViewColumn column) { SortOrder direction = column.HeaderCell.SortGlyphDirection; if (column.HeaderCell is MyDataGridViewColumnHeaderCell) { direction = ((MyDataGridViewColumnHeaderCell)column.HeaderCell).SortOrderDirection; } return column.DataPropertyName + " " + (direction == SortOrder.Descending ? "DESC" : "ASC"); } } public partial class MyDataGridViewColumnHeaderCell : DataGridViewColumnHeaderCell { public SortOrder SortOrderDirection { get; set; } // defaults to zero = SortOrder.None; protected override void Paint(System.Drawing.Graphics graphics, System.Drawing.Rectangle clipBounds, System.Drawing.Rectangle cellBounds, int rowIndex, DataGridViewElementStates dataGridViewElementState, object value, object formattedValue, string errorText, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle, DataGridViewPaintParts paintParts) { this.SortGlyphDirection = this.SortOrderDirection; base.Paint(graphics, clipBounds, cellBounds, rowIndex, dataGridViewElementState, value, formattedValue, errorText, cellStyle, advancedBorderStyle, paintParts); } public override object Clone() { MyDataGridViewColumnHeaderCell result = (MyDataGridViewColumnHeaderCell)base.Clone(); result.SortOrderDirection = this.SortOrderDirection; return result; } protected override void OnClick(DataGridViewCellEventArgs e) { this.SortOrderDirection = (this.SortOrderDirection != SortOrder.Ascending) ? SortOrder.Ascending : SortOrder.Descending; base.OnClick(e); } } public static partial class Extensions { public static void ForEach<T>(this IEnumerable<T> value, Action<T> action) { foreach (T item in value) { action(item); } } } }
Я никогда не отвечал на вопросы здесь, поэтому прошу прощения, если формат неверен, но я нашел ответ на этот вопрос, который может быть проще для будущих посетителей. (См. http://www.pcreview.co.uk/threads/datagridview-glyphs.3145090/ )
Dim dictionarySortColumns As New Dictionary(Of String, Integer) Private Sub DataGridViewFileLoader_Sorted(sender As Object, e As EventArgs) Handles DataGridViewFileLoader.Sorted Dim dv As New DataView(dataSetLoadScreener.Tables(0)) Dim columnHeader As String = DataGridViewFileLoader.SortedColumn.Name Dim sortDirection As Integer = DataGridViewFileLoader.SortOrder Dim sortcode As String = "" Dim sortOrder As String = "" If sortDirection = 1 Then sortOrder = "ASC" Else sortOrder = "DESC" End If If dictionarySortColumns.ContainsKey(columnHeader) Then dictionarySortColumns.Remove(columnHeader) End If sortcode = columnHeader + " " + sortOrder For Each colHeader As String In dictionarySortColumns.Keys If dictionarySortColumns(colHeader) = 1 Then sortOrder = "ASC" Else sortOrder = "DESC" End If sortcode = sortcode + "," + colHeader + " " + sortOrder Next dictionarySortColumns.Add(columnHeader, sortDirection) dv.Sort = sortcode DataGridViewFileLoader.DataSource = Nothing DataGridViewFileLoader.DataSource = dv formatDataGridViewFileLoader() End Sub Private Sub DataGridViewFileLoader_CellPainting(sender As Object, e As DataGridViewCellPaintingEventArgs) Handles DataGridViewFileLoader.CellPainting Dim sOrder As System.Windows.Forms.SortOrder For Each key As String In dictionarySortColumns.Keys If dictionarySortColumns(key) = 1 Then sOrder = Windows.Forms.SortOrder.Ascending Else sOrder = Windows.Forms.SortOrder.Descending End If DataGridViewFileLoader.Columns(key).HeaderCell.SortGlyphDirection = sOrder Next End Sub
Comments