Сортировать 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 источник данных, источник данных может быть отсортирован по нескольким столбцам и 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