Сортировать представление 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";
}
}
}
1154   4  

4 ответов:

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

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

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


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

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

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

Когда DataGridView привязывается к источнику данных (DataView, BindingSource, Table, DataSet+ "tablename" ) во всех случаях он ссылается на DataView. Получить ссылку на этот DataView и установить SortFilter), Как вы хотите:

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

    Ничего не найдено.