如何在用户单击列标题时启用DataGridView排序?

时间:2011-04-05 14:01:39

标签: c# winforms sorting datagridview

我的表单上有一个datagridview,我填充它:

dataGridView1.DataSource = students.Select(s => new { ID = s.StudentId, RUDE = s.RUDE, Nombre = s.Name, Apellidos = s.LastNameFather + " " + s.LastNameMother, Nacido = s.DateOfBirth })
                                   .OrderBy(s => s.Apellidos)
                                   .ToList();

现在,我使用s.Apellidos作为默认排序,但我也希望允许用户在单击列标题时进行排序。

这种以任何方式修改数据,只是客户端奖励,以便在用眼睛扫描屏幕时更容易搜索信息。

感谢您的建议。

15 个答案:

答案 0 :(得分:48)

将所有列(可由用户排序)SortMode属性设置为Automatic

dataGridView1.DataSource = students.Select(s => new { ID = s.StudentId, RUDE = s.RUDE, Nombre = s.Name, Apellidos = s.LastNameFather + " " + s.LastNameMother, Nacido = s.DateOfBirth })
                                   .OrderBy(s => s.Apellidos)
                                   .ToList();

    foreach(DataGridViewColumn column in dataGridView1.Columns)
    {

        column.SortMode = DataGridViewColumnSortMode.Automatic;
    }

编辑:由于您的datagridview与linq查询绑定,因此不会对其进行排序。所以请通过这个link来解释如何创建一个可排序的绑定列表,然后将其作为数据源提供给datagridview。

答案 1 :(得分:26)

正如Niraj建议的那样,使用 SortableBindingList 。我已经非常成功地使用了DataGridView。

以下是我使用的更新代码的链接 - Presenting the SortableBindingList - Take Two

只需将两个源文件添加到您的项目中,您就可以开展业务了。

来源位于SortableBindingList.zip

答案 2 :(得分:7)

您的数据网格首先需要绑定到可排序列表。

创建此事件处理程序:

    void MakeColumnsSortable_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e)
    {
        //Add this as an event on DataBindingComplete
        DataGridView dataGridView = sender as DataGridView;
        if (dataGridView == null)
        {
            var ex = new InvalidOperationException("This event is for a DataGridView type senders only.");
            ex.Data.Add("Sender type", sender.GetType().Name);
            throw ex;
        }

        foreach (DataGridViewColumn column in dataGridView.Columns)
            column.SortMode = DataGridViewColumnSortMode.Automatic;
    }

并初始化每个datragrids的事件,如下所示:

        dataGridView1.DataBindingComplete += MakeColumnsSortable_DataBindingComplete;

答案 3 :(得分:5)

您可以像这样使用DataGridViewColoumnHeaderMouseClick事件:

Private string order = String.Empty;
private void dgvDepartment_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)
{
    if (order == "d")
{
        order = "a";                
dataGridView1.DataSource = students.Select(s => new { ID = s.StudentId, RUDE = s.RUDE, Nombre = s.Name, Apellidos = s.LastNameFather + " " + s.LastNameMother, Nacido = s.DateOfBirth })   .OrderBy(s => s.Apellidos).ToList();
    }
    else
    {
        order = "d";
        dataGridView1.DataSource = students.Select(s => new { ID = s.StudentId, RUDE = s.RUDE, Nombre = s.Name, Apellidos = s.LastNameFather + " " + s.LastNameMother, Nacido = s.DateOfBirth }.OrderByDescending(s => s.Apellidos)  .ToList()
    }
}

答案 4 :(得分:5)

您无需创建绑定数据源。如果您想对所有列应用排序,这是我的更通用的解决方案;

enableMouseTracking

确保您将数据网格订阅到活动private int _previousIndex; private bool _sortDirection; private void gridView_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e) { if (e.ColumnIndex == _previousIndex) _sortDirection ^= true; // toggle direction gridView.DataSource = SortData( (List<MainGridViewModel>)gridReview.DataSource, gridReview.Columns[e.ColumnIndex].Name, _sortDirection); _previousIndex = e.ColumnIndex; } public List<MainGridViewModel> SortData(List<MainGridViewModel> list, string column, bool ascending) { return ascending ? list.OrderBy(_ => _.GetType().GetProperty(column).GetValue(_)).ToList() : list.OrderByDescending(_ => _.GetType().GetProperty(column).GetValue(_)).ToList(); } 。当用户点击列时,它将按降序排序。如果再次单击相同的列标题,则将按升序应用排序。

答案 5 :(得分:3)


在使用Entity Framework(在这种情况下为版本6)时,有一个非常简单的解决方案。我不确定,但似乎ObservableCollectionExtensions.ToBindingList<T>方法返回可排序绑定列表的实现。我没有找到确认此假设的源代码,但是从此方法返回的对象非常适用于DataGridView,尤其是在通过单击其标题对列进行排序时。

代码非常简单,只依赖于.net和实体框架类:

using System.Data.Entity;

IEnumerable<Item> items = MethodCreatingItems();

var observableItems = new System.Collections.ObjectModel.ObservableCollection<Item>(items);
System.ComponentModel.BindingList<Item> source = observableItems.ToBindingList();

MyDataGridView.DataSource = source;

答案 6 :(得分:3)

另一种方法是使用“System.Linq.Dynamic”库。您可以从Nuget获取此库。无需任何自定义实现或可排序列表:)

import pandas as pd
import numpy as np
results = pd.DataFrame({'scores':[78.5, 91.0, 103.5], 'outcomes':[1,0,1]})
thresholds = [4.7562029077978352, 4.6952820449271861, 4.6343611820565371, 4.5734403191858881, 103.5, 98.5, 93.5, 88.5, 83.5, 78.5]
thresholds_col = ['{:.16f}'.format(e) for e in thresholds]
data = results.outcomes[:,np.newaxis] * ((results.scores[:,np.newaxis] - thresholds > 0))
results = results.join(pd.DataFrame(data=data, columns=thresholds_col))
print results
print results[thresholds_col]

Out[79]: 
   4.7562029077978352  4.6952820449271861  4.6343611820565371  \
0                   1                   1                   1   
1                   0                   0                   0   
2                   1                   1                   1   

   4.5734403191858881  103.5000000000000000  98.5000000000000000  \
0                   1                     0                    0   
1                   0                     0                    0   
2                   1                     0                    1   

   93.5000000000000000  88.5000000000000000  83.5000000000000000  \
0                    0                    0                    0   
1                    0                    0                    0   
2                    1                    1                    1   

   78.5000000000000000  
0                    0  
1                    0  
2                    1 

答案 7 :(得分:2)

KISS:保持简单,愚蠢

方式A: 喜欢使用 DataBinding 排序时实现自己的SortableBindingList类。

方式B: 使用列表&lt;字符串&gt; 排序也可以,但不适用于 DataBinding

答案 8 :(得分:1)

  1. 创建一个包含所需属性的类,并在构造函数中填充它们

    class Student
    {
        int _StudentId;
        public int StudentId {get;}
        string _Name;
        public string Name {get;}
        ...
    
        public Student(int studentId, string name ...)
        { _StudentId = studentId; _Name = name; ... }
    }
    
  2. 创建IComparer&lt;学生&gt; class,能够排序

    class StudentSorter : IComparer<Student>
    {
        public enum SField {StudentId, Name ... }
        SField _sField; SortOrder _sortOrder;
    
        public StudentSorder(SField field, SortOrder order)
        { _sField = field; _sortOrder = order;}
    
        public int Compare(Student x, Student y)
        {
            if (_SortOrder == SortOrder.Descending)
            {
                Student tmp = x;
                x = y;
                y = tmp;
            }
    
            if (x == null || y == null)
                return 0;
    
            int result = 0;
            switch (_sField)
            {
                case SField.StudentId:
                    result = x.StudentId.CompareTo(y.StudentId);
                    break;
                case SField.Name:
                    result = x.Name.CompareTo(y.Name);
                    break;
                    ...
            }
    
            return result;
        }
    }
    
  3. 在包含datagrid add

    的表单中
    ListDictionary sortOrderLD = new ListDictionary(); //if less than 10 columns
    private SortOrder SetOrderDirection(string column)
    {
        if (sortOrderLD.Contains(column))
        {
            sortOrderLD[column] = (SortOrder)sortOrderLD[column] == SortOrder.Ascending ? SortOrder.Descending : SortOrder.Ascending;
        }
        else
        {
            sortOrderLD.Add(column, SortOrder.Ascending);
        }
    
        return (SortOrder)sortOrderLD[column];
    }
    
  4. 在datagridview_ColumnHeaderMouseClick事件处理程序中执行类似这样的操作

    private void dgv_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)
    {
        StudentSorter sorter = null;
        string column = dGV.Columns[e.ColumnIndex].DataPropertyName; //Use column name if you set it
        if (column == "StudentId")
        {
            sorter = new StudentSorter(StudentSorter.SField.StudentId, SetOrderDirection(column));
        }
        else if (column == "Name")
        {
            sorter = new StudentSorter(StudentSorter.SField.Name, SetOrderDirection(column));
        }
    
        ...
    
        List<Student> lstFD = datagridview.DataSource as List<Student>;
        lstFD.Sort(sorter);
        datagridview.DataSource = lstFD;
        datagridview.Refresh();
    }
    
  5. 希望这有帮助

答案 9 :(得分:1)

如果收到类似

的错误消息
  

类型'System.NullReferenceException'的未处理异常   发生在System.Windows.Forms.dll

如果您使用SortableBindingList,您的代码可能会在DataGridView行上使用一些循环,并尝试访问空的最后一行! (BindingSource = null)

如果您不需要允许用户直接在DataGridView中添加新行,这行代码可以轻松解决问题:

InitializeComponent();
m_dataGridView.AllowUserToAddRows = false; // after components initialized
...

答案 10 :(得分:0)

万一有人还在寻找它,我是在VS 2008 C#上做到的。

在Event ColumnHeaderMouseClick上,为gridview添加数据绑定,并像参数一样按字段发送订单。您可以按如下方式获取单击的字段:

dgView.Columns[e.ColumnIndex].Name

在我的例子中,标题的名称与视图字段名称类似。

答案 11 :(得分:0)

我有一个BindingList&lt;&gt; object绑定为dataGridView的数据源。

BindingList x1;
x1 = new BindingList<sourceObject>();
BindingSource bsx1 = new BindingSource();
bsx1.DataSource = x1;
dataGridView1.DataSource = bsx1;

单击列标题时,不会进行排序。 我使用了Tom Bushell提供的SortableBindingList答案。 已将两个源文件包含到我的项目中

  1. SortableBindingList.cs
  2. PropertyComparer.cs
  3. 然后对我的代码进行了更改:

    Be.Timvw.Framework.ComponentModel.SortableBindingList x1;                       // 1
    x1 = new Be.Timvw.Framework.ComponentModel.SortableBindingList<sourceObject>(); // 2
    BindingSource bsx1 = new BindingSource();
    bsx1.DataSource = x1;
    dataGridView1.DataSource = bsx1;
    

    在这些更改后,我对我的程序进行了构建。我现在可以通过单击列标题进行排序。只需要更改两行,它们在上面的代码片段中通过尾随注释突出显示。

答案 12 :(得分:0)

我建议使用DataTable.DefaultView作为DataSource。然后是下面的一行。

foreach (DataGridViewColumn column in gridview.Columns)
    {
       column.SortMode = DataGridViewColumnSortMode.Automatic;
    }

之后gridview本身将管理排序(支持Ascending或Descending。)

答案 13 :(得分:0)

将此行放在您的Windows窗体中(在加载时或更好地使用公共方法,例如&#34; binddata&#34;):

//
// bind the data and make the grid sortable 
//
this.datagridview1.MakeSortable( myenumerablecollection ); 

将此代码放在名为DataGridViewExtensions.cs(或类似)

的文件中
// MakeSortable extension. 
// this will make any enumerable collection sortable on a datagrid view.  

//
// BEGIN MAKESORTABLE - Mark A. Lloyd
//
// Enables sort on all cols of a DatagridView 

//



    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    using System.Windows.Forms;

    public static class DataGridViewExtensions
    {
    public static void MakeSortable<T>(
        this DataGridView dataGridView, 
        IEnumerable<T> dataSource,
        SortOrder defaultSort = SortOrder.Ascending, 
        SortOrder initialSort = SortOrder.None)
    {
        var sortProviderDictionary = new Dictionary<int, Func<SortOrder, IEnumerable<T>>>();
        var previousSortOrderDictionary = new Dictionary<int, SortOrder>();
        var itemType = typeof(T);
        dataGridView.DataSource = dataSource;
        foreach (DataGridViewColumn c in dataGridView.Columns)
        {
            object Provider(T info) => itemType.GetProperty(c.Name)?.GetValue(info);
            sortProviderDictionary[c.Index] = so => so != defaultSort ? 
                dataSource.OrderByDescending<T, object>(Provider) : 
                dataSource.OrderBy<T,object>(Provider);
            previousSortOrderDictionary[c.Index] = initialSort;
        }

        async Task DoSort(int index)
        {

            switch (previousSortOrderDictionary[index])
            {
                case SortOrder.Ascending:
                    previousSortOrderDictionary[index] = SortOrder.Descending;
                    break;
                case SortOrder.None:
                case SortOrder.Descending:
                    previousSortOrderDictionary[index] = SortOrder.Ascending;
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }

            IEnumerable<T> sorted = null;
            dataGridView.Cursor = Cursors.WaitCursor;
            dataGridView.Enabled = false;
            await Task.Run(() => sorted = sortProviderDictionary[index](previousSortOrderDictionary[index]).ToList());
            dataGridView.DataSource = sorted;
            dataGridView.Enabled = true;
            dataGridView.Cursor = Cursors.Default;

        }

        dataGridView.ColumnHeaderMouseClick+= (object sender, DataGridViewCellMouseEventArgs e) => DoSort(index: e.ColumnIndex);
    }
}

答案 14 :(得分:0)

在我的情况下,问题是我将DataSource设置为object,这就是为什么它没有被排序的原因。从object更改为DataTable后,它可以很好地工作,无需任何代码补充。