首页 文章

在多列上对DataGridView进行排序?

提问于
浏览
8

我已经搜索了一个在多列上对DataGridView进行排序的示例,但似乎无法找到符合我想要的示例 .

基本上,我有一个绑定的DataGridView控件(绑定到DataTable / DataView),绑定的DataTable有两列: - priority和date . 我想按优先顺序按日期排序 . 也就是说,优先级列采用优先级,然后是日期,但两者都可以是升序或降序 .

因此,例如,我可能具有低优先级,早期日期优先(按优先级asc,日期asc排序),并且通过单击日期列 Headers ,切换到低优先级,晚期日期优先(按优先级顺序asc,日期desc) . 如果我然后单击优先级,我希望首先具有高优先级,然后是较晚的日期(日期列的当前排序顺序 - 按优先级desc排序,日期desc),但是然后可以单击日期列 Headers 切换到高优先级,早期日期(按优先级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";
     }
  }
}

5 回答

  • 2

    这是一个像google电子表格一样的示例 - 单击每列按此列排序,然后再次单击列交换搜索方向 . 此外,它按照您单击列的历史记录的相反顺序进行搜索,即如果单击列D然后是B然后是C然后是A,在最后一次点击它将按A,C,B,D列排序,并在最近点击该列时搜索的每个方向排序 .

    它依赖于DataGridView内置的这些功能:

    • 如果DataGridView有一个DataView作为其DataSource,那么为该DataView设置Sort字符串会立即导致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");
        }
    }
    

    然后将我们的Sorter对象收集到Sorters类中:

    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
    
  • 4

    我第一次看到这个,我完全错过了同时按多列排序的部分(我的错,不是你的;问题非常清楚) .

    如果是这种情况,您将不得不编写自己处理此问题的代码 . 默认情况下,提供的 DataGridView 控件不支持多列排序 . 幸运的是,其他人已经为你实现了很多工作 . 以下是一些示例:

    或者,如果将 DataGridView 绑定到数据源,则可以在多个列上对该数据源进行排序,并且 DataGridView 控件将遵循该排序 . 任何实现 IBindingListView 并公开 Sort 属性的数据源都可用于多列排序 .

    但是,无论您选择启用多列排序的路径如何,在强制 DataGridView 在多列上显示排序箭头标志符号方面都不会有太大成功 . 这里最简单的解决方案是自定义绘制列 Headers 以提供您自己的排序标志符号 .

    为此,请将处理程序附加到DataGridView.CellPainting event并检查 RowIndex 为-1(表示列 Headers ) . 有一个所有者绘制的列 Headers here的完整示例 . 我强烈建议坚持使用传统的箭头图标,但一旦你走这条路线,选项真的是无限的 . 您可以使列 Headers 看起来像您想要的任何内容,甚至可以使用不同的图标指示排序顺序中每列的相对权重 .

    您还可以选择从DataGridViewColumnHeaderCell派生新类并覆盖其Paint method . 这可能是一种更清洁,更面向对象的方式来完成同样的事情 .

  • 3

    DataGridView 在所有情况下都绑定到 DataSource (DataView,BindingSource,Table,DataSet "tablename"),它引用 DataView . 获取对此DataView的引用并根据需要设置 Sort (和 Filter ):

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

    注意: Column names used in Sort and Filter correspond to the column names in DataTable ,DataGridView中的列名是用于绑定的基础数据属性名称(类的属性名称,DataTables的列名称等) . 您可以像这样在DataView中使用列名:

    string colName = dgv.Columns[colIdx].DataPropertyName
    

    取决于您如何跟踪排序列(colSequence,colName,asc / desc,dgvColIdx),您可以决定如何构建Sort和Filter表达式并在dgv中设置SortGlyph(为简单起见,我制作了硬编码) .

  • 0

    好 .

    根据Cody的上述建议,我现在有了一些似乎按预期工作的东西 . 我已经对HeaderCell进行了分类并覆盖了Paint方法(但是通过在base.Paint之前立即设置SortGlyphDirection来欺骗),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); } }
      }
    }
    
  • 7

    我在这里从来没有回答过一个问题,所以如果格式不正确我会道歉,但我找到了这个问题的答案,这对未来的访问者来说可能更简单 . (见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
    

相关问题