Сортировать DataGridView по нескольким столбцам? - PullRequest
8 голосов
/ 10 января 2011

Я искал пример сортировки DataGridView по нескольким столбцам, но, похоже, не смог найти пример, который делает то, что я хотел бы.

По сути, у меня есть связанный DataGridViewэлемент управления (связанный с DataTable / DataView), а связанный DataTable имеет два столбца: - приоритет и дата.Я хотел бы отсортировать по дате в рамках приоритета.То есть столбец приоритета имеет приоритет, затем его дата, но оба могут быть в порядке возрастания или убывания.

Так, например, у меня может быть низкий приоритет, ранняя дата сначала (порядок по приоритету asc,date asc) и, щелкнув по заголовку столбца даты, переключиться на низкий приоритет, с более поздней датой сначала (порядок по приоритету asc, date desc) .Если затем я щелкну по приоритету, сначала я бы хотел иметь высокий приоритет, а затем позднюю дату (текущий порядок сортировки для столбца даты - порядок по приоритету desc, date desc) , но затем я смогущелкните заголовок столбца даты, чтобы переключиться на высокий приоритет, раннюю дату (порядок по приоритету desc, date 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";
     }
  }
}

Ответы [ 5 ]

7 голосов
/ 11 января 2011

Когда я впервые прочитал это, я полностью пропустил часть о сортировке по нескольким столбцам одновременно (моя вина, а не ваша; вопрос был совершенно ясен).

Если это так, вам придется написать код, который обрабатывает это самостоятельно. Предоставленный элемент управления DataGridView по умолчанию не поддерживает сортировку по нескольким столбцам. К счастью, другие уже проделали большую работу, чтобы реализовать это для вас. Вот несколько образцов:

В качестве альтернативы, если вы связываете DataGridView с источником данных, этот источник данных может быть отсортирован по нескольким столбцам, и элемент управления DataGridView будет учитывать эту сортировку. Любой источник данных, который реализует IBindingListView и предоставляет свойство Sort, будет работать для сортировки по нескольким столбцам.


Тем не менее, независимо от того, какой маршрут вы выберете, чтобы включить многостолбцовую сортировку, вы не добьетесь больших успехов в принуждении DataGridView к отображению символа стрелки сортировки в нескольких столбцах. Самым простым решением здесь является выборочная отрисовка только заголовков столбцов для предоставления собственного символа сортировки.

Для этого присоедините обработчик к событию DataGridView.CellPainting и проверьте наличие RowIndex -1 (что указывает на заголовок столбца). Здесь приведен полный образец заголовков столбцов, нарисованных владельцем здесь . Я настоятельно рекомендую придерживаться обычного значка со стрелкой, но как только вы пойдете по этому пути, варианты действительно безграничны. Вы можете сделать заголовки столбцов такими, какими хотите, и даже указать относительный вес каждого столбца в порядке сортировки, используя различные значки.

Вы также можете выбрать новый класс из DataGridViewColumnHeaderCell и переопределить его Paint метод . Вероятно, это более чистый, более объектно-ориентированный способ достижения того же самого.

4 голосов
/ 22 февраля 2012

Когда DataGridView связывается с DataSource ( 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), вы можете решить, как создать Sort и Filter.выражение и установить SortGlyph в dgv (я сделал жесткий код для простоты).

3 голосов
/ 11 января 2011

ОК.

Исходя из приведенных выше советов Коди, у меня теперь есть кое-что, что, кажется, работает как положено.Я подклассифицировал HeaderCell и переопределил метод Paint (но обманул, установив SortGlyphDirection непосредственно перед base.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); } }
  }
}
2 голосов
/ 05 июня 2015

Я никогда не отвечал на вопрос здесь, поэтому я прошу прощения, если формат неправильный, но я нашел ответ на этот вопрос, который может быть проще для будущих посетителей.(См. 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
0 голосов
/ 23 ноября 2018

Вот пример, который работает как электронные таблицы Google: щелчок по заголовку каждого столбца сортирует по этому столбцу и повторный щелчок по столбцу меняет направление поиска. Кроме того, он ищет в обратном порядке историю того, как вы щелкнули по столбцам, т. Е. Если вы щелкнете по столбцу D, затем B, затем C, затем A, при последнем щелчке он будет сортироваться по столбцам A, C, B, D и в направлении для каждого, что он искал, когда вы последний раз щелкнули по этому столбцу.

Он опирается на эти функции, встроенные в DataGridView:

  • Если DataGridView имеет DataView в качестве своего DataSource, тогда установка строки сортировки для этого DataView вызывает немедленную сортировку DataView, и эта сортировка сразу отображается в связанном элементе управления DataGridView
  • Строка сортировки может содержать несколько столбцов, каждый из которых может быть указан для сортировки ASC или DESC
  • Когда вы устанавливаете строку сортировки в DataView (тем самым вызывая его сортировку), DataGridView, к которому он привязан, автоматически добавит соответствующий глиф вверх-вниз на основе первого элемента (имя и направление) в вашем виде. строка

Начнем с сортировщика:

internal class Sorter 
{
    internal readonly string ColumnName;
    internal bool IsAscending;

    internal Sorter(string columnName, bool isAscending)
    {
        this.ColumnName = columnName;
        this.IsAscending = isAscending;
    }

    public override bool Equals(object other)
    {  // For equivalence, compare column name only (not object ref or sort order)
        if (other == null) { return false; }
        if (other.GetType() != typeof(Sorter)) { return false; }
        return this.ColumnName == ((Sorter)other).ColumnName;
    }

    public override int GetHashCode()
    { // required if we have overridden Equals
        return this.ColumnName.GetHashCode();
    }

    public override string ToString()
    {
        return this.ColumnName + (this.IsAscending ? " ASC" : " DESC");
    }
}

А затем соберите наши объекты Сортировщика в класс Сортировщиков:

using System.Collections.Generic;
using System.Linq;

internal class Sorters : List<Sorter>
{
    internal void BringColumnToFrontOfSortingOrder(Sorter sorter)
    {
        if (this.Contains<Sorter>(sorter))
        {
            this.Remove(sorter); // remove it from where it is
        }
        // put it at the start
        this.Insert(0, sorter);
    }

    // Returns a DataView sorting string like "ColName1 ASC, ColName2 DESC" etc
    public override string ToString()
    {
        var s = new System.Text.StringBuilder();
        foreach (Sorter sorter in this)
        {
            if (s.Length > 0) { s.Append(", "); }
            s.Append(sorter.ToString());
        }
        return s.ToString();
    }
}

И я создаю свой собственный класс MultiSortingDataGridView, производный от DataGridView, с переопределенным методом Sort, который отслеживает, по каким столбцам вы щелкнули, и вызывает своего рода DataView, который является связанным DataSource моей сетки на основе истории. кликов сортировки вашего столбца:

using System.Data;
using System.Windows.Forms;
using System.ComponentModel;

public class MultiSortingDataGridView : DataGridView // derive from DataGridView and extend it
{
    private Sorters _sorters = new Sorters(); // keeping track of what column(s) we have searched by

    // override the regular search with our super duper multi-column search
    public override void Sort(DataGridViewColumn dataGridViewColumn, ListSortDirection direction)
    {
        Sorter sorter = new Sorter(dataGridViewColumn.Name, direction == ListSortDirection.Ascending);
        this._sorters.BringColumnToFrontOfSortingOrder(sorter);

        // Get the data view that is our data source
        DataView vw = (DataView)this.DataSource;

        // When you set the Sort property, it causes it to sort, and happily it sets
        // the up/down glyph of the column that corresponds to your first sort-by item.
        vw.Sort = this._sorters.ToString();
    }
}

Затем в своей форме я проверяю, что я использую элемент управления MultiSortingDataGridView (как указано выше) вместо обычного DataGridView, и я устанавливаю его источник данных следующим образом:

DataTable tbl = SomeMethodForGettingMyTabularData(); // instantiate a data table with the data you want to show
DataView vw = new DataView(tbl); // make a data view from it
this.dgv.DataSource = vw; // bind the view as data source to my MultiSortingDataGridView control
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...