Sorting DataGridView for multiple columns?

I was looking for an example of sorting a DataGridView on multiple columns, but I can't seem to find an example that does what I would like.

Basically, I have a related DataGridView control (bound to a DataTable / DataView), and the associated DataTable has two columns: - priority and date. I would like to sort by date in priority order. That is, the priority column takes a preliminary estimate, then its date, but both can be ascending or descending.

So, for example, I can have a low priority, an early date first (asc priority order, ascending date) and, clicking on the heading of a date column, switch to low priority, late date first (asc priority order, desc date). If I then click on priority, first I want to have a high priority, and then a late date (the current sort order for the date column is the desc, date desc priority order), but then you can click the heading of the date column to switch to high priority, to early date (desc. order, ascending date).

Ideally, I would like the glyphs on both columns to be displayed in both ascending and descending order.

Any ideas or pointers would be greatly appreciated.

This (see below) seems pretty close, but the glyphs are not working correctly.

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"; } } } 
+8
sorting c # datagridview
source share
4 answers

Ok

Following Cody's suggestions above, I now have something that seems to work as expected. I subclassed HeaderCell and overloaded the Paint method (but tricked it by setting SortGlyphDirection just before base.Paint), and DGV now draws a few sort glyphs.

 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); } } } } 
+3
source share

The first time I read this, I completely skipped the sorting part of several columns at the same time (my fault, not yours, the question was completely clear).

If this happens, you will have to write code that processes it yourself. The provided DataGridView control does not support sorting by multiple columns by default. Fortunately, others have already done a great job of implementing this for you. Here are some examples:

Alternatively, if you bind your DataGridView to a data source, this data source can be sorted by several columns, and the DataGridView control will abide by this sorting. Any data source that implements IBindingListView and provides a Sort property will work to sort multiple columns.


However, regardless of the route that you have chosen to enable multi-column sorting, you will not be very successful in forcing the DataGridView display the sort glyph character in multiple columns. The easiest solution here is to custom draw only the column headers to provide your own sort glyph.

To do this, attach a handler to the DataGridView.CellPainting event and check RowIndex for -1 (by specifying the column heading). Here is a complete sample of the owner column headings here . I highly recommend sticking with the regular arrow icon, but as you go along this route, the options are really unlimited. You can make the column headings look like anything you want, and even indicate the relative weight of each column in sort order using different icons.

You can also select a new class from the DataGridViewColumnHeaderCell and override its Paint . This is probably a cleaner, more object oriented way to achieve the same.

+7
source share

When a DataGridView is associated with a DataSource (DataView, BindingSource, Table, DataSet + "tablename"), in all cases it refers to a DataView . Get a link to this DataView and set the Sort (and Filter ) as you wish:

 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; } 

Note. The column names used in Sort and Filter correspond to the column names in the DataTable , Column names in the DataGridView are the base data property names that are used for binding (property names for classes, column names for DataTables, etc.). You can get the column name used in the DataView as follows:

 string colName = dgv.Columns[colIdx].DataPropertyName 

Depending on how you want to track the sorted columns (colSequence, colName, asc / desc, dgvColIdx), you can decide how to build a Sort and Filter expression and set SortGlyph to dgv (I made hardcode for simplicity).

+4
source share

I never answered the question here, so I apologize if the format is incorrect, but I found an answer to this question, which may be easier for future visitors. (See 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 
+2
source share

Source: https://habr.com/ru/post/650873/


All Articles