Сортировать DataGridView по нескольким столбцам?
Я искал пример сортировки DataGridView по нескольким столбцам, но, похоже, не смог найти пример, который делает то, что я хотел бы.
в основном, у меня есть связанный элемент управления DataGridView (связанный с DataTable/DataView), а связанный DataTable имеет два столбца:- priority и date. Я хотел бы отсортировать по дате в рамках приоритета. То есть столбец приоритета принимает precendence, затем его дата, но оба могут быть восходящими или нисходящими.
Так, для например, у меня может быть низкий приоритет, ранняя дата first (порядок по приоритету asc, дата asc), и, щелкнув заголовок столбца даты, переключитесь на низкий приоритет, сначала поздняя дата (порядок по приоритету asc, дата desc). Если я затем нажму на приоритет, я хотел бы сначала иметь высокий приоритет, а затем позднюю дату (текущий порядок сортировки для столбца даты - заказ по приоритету desc, дата desc), но затем можно щелкнуть заголовок столбца даты, чтобы переключиться на высокий приоритет, ранняя дата (порядок по приоритету 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
control не поддерживает многоколоночную сортировку по умолчанию. К счастью, другие уже проделали большую работу, чтобы реализовать это для вас. Вот несколько образцы:
- сортировка нескольких столбцов DataGridView (CodeProject)
- как разрешить сортировку по нескольким столбцам в пользовательской привязке данных (CodeProject)
альтернативно, если вы связываете свой DataGridView
источник данных, источник данных может быть отсортирован по нескольким столбцам и DataGridView
control будет уважать эту сортировку. Любой источник данных, реализующий IBindingListView
и предоставляет Sort
свойство будет работать для многоколоночная сортировка.
Однако, независимо от маршрута, который вы выберете для включения многоколоночной сортировки, у вас не будет большого успеха в принуждении DataGridView
для отображения глифа стрелки сортировки на нескольких столбцах. Самое простое решение здесь-пользовательское рисование только заголовков столбцов, чтобы предоставить свой собственный глиф сортировки.
для этого прикрепите обработчик к DataGridView.CellPainting
событие и RowIndex
-1 (с указанием заголовка столбца). Существует полный образец заголовков столбцов, нарисованных владельцем здесь. Я настоятельно рекомендую придерживаться обычного значка стрелки, но как только вы идете по этому маршруту, варианты действительно неограниченны. Вы можете сделать заголовки столбцов похожими на все, что вы хотите, и даже указать относительный вес каждого столбца в порядке сортировки с помощью разных значков.
вы также можете выбрать для получения нового класса из DataGridViewColumnHeaderCell
и переопределить его Paint
метод. Это, вероятно, более чистый, более объектно-ориентированный способ достижения того же самого.
, когда DataGridView привязывается к источник (DataView, BindingSource, Table, DataSet+ "tablename") во всех случаях это относится к DataView. Получите ссылку на этот DataView и установите вроде (и фильтр), как вы хотите:
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 являются базовыми данными имена свойств, используемые для привязки (имена свойств для классов, имена столбцов для DataTables и т. д.). Вы можете получить имя столбца, используемое в DataView следующим образом:
string colName = dgv.Columns[colIdx].DataPropertyName
зависит от того, как вы хотите отслеживать отсортированные столбцы (colSequence, colName, asc/desc, dgvColIdx), вы можете решить, как построить выражение сортировки и фильтра и установить SortGlyph в dgv (я сделал hardcode для простоты).
Ok.
следуя предложениям Коди выше, у меня теперь есть что-то, что, похоже, работает так, как ожидалось. Я подкласс 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