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


74

我在表单上有一个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作为默认排序,但是我也想允许用户在单击列标题时进行排序。

这种排序不会以任何方式修改数据,这只是客户端的好处,可以使他们在用眼睛扫描屏幕时更轻松地搜索信息。

感谢您的建议。


Answers:


51

将所有列的(可以由用户排序)的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查询绑定,它将不会被排序。因此,请仔细阅读本文[404 dead link, see next section],它说明了如何创建可排序的绑定列表,然后将其作为数据源提供给datagridview。

从死链接中恢复的代码

上面的链接是404死点。我从该页面的Internet Wayback Machine存档中恢复了代码。

public Form1()
{
    InitializeComponent();

    SortableBindingList<person> persons = new SortableBindingList<person>();
    persons.Add(new Person(1, "timvw", new DateTime(1980, 04, 30)));
    persons.Add(new Person(2, "John Doe", DateTime.Now));

    this.dataGridView1.AutoGenerateColumns = false;
    this.ColumnId.DataPropertyName = "Id";
    this.ColumnName.DataPropertyName = "Name";
    this.ColumnBirthday.DataPropertyName = "Birthday";
    this.dataGridView1.DataSource = persons;
}

由于我希望所有列都是可排序的,因此有没有办法以编程方式遍历每个列并设置此属性?

@Niraj:我收到编译器错误。我尝试将您的答案更改为column.Name,但它似乎没有以任何方式修改排序。

2
@Sergio:因此,如果我理解正确,那么您现在没有任何错误,但是列仍然无法排序。
元帅,

2
@Niraj:是的。我单击列标题,但行本身未排序。

4
顺便说一句,您foreach可能会更简单:foreach(DataGridViewColumn column in dataGridView1.Columns) column.SortMode = DataGridViewColumnSortMode.Automatic;
Tim Schmelter 2014年

28

如Niraj所建议,请使用SortableBindingList。我已经非常成功地使用了DataGridView。

这是我使用的更新代码的链接- 呈现SortableBindingList-带两个-存档

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

来源在SortableBindingList.zip中-404 dead link


该解决方案不需要LINQ,并且可以在.net 2.0中使用,并且在BindingList中还可以找到其他有用的功能,例如更改通知。感谢分享。
Mehran

1
源不再存在,是否有任何更新的源?
2016年

正是我想要的。谢谢。
马特·马丁

1
@Arend我将链接添加到该死链接的回送机器存档中
Walter Stabosz

10

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

using System.Linq.Dynamic;
private bool sortAscending = false;

private void dataGridView_ColumnHeaderMouseClick ( object sender, DataGridViewCellMouseEventArgs e )
{
    if ( sortAscending )
        dataGridView.DataSource = list.OrderBy ( dataGridView.Columns [ e.ColumnIndex ].DataPropertyName ).ToList ( );
    else
        dataGridView.DataSource = list.OrderBy ( dataGridView.Columns [ e.ColumnIndex ].DataPropertyName ).Reverse ( ).ToList ( );
    sortAscending = !sortAscending;
}

1
像魅力一样运作,不敢相信还没有人给您积极的反馈
Scope Creep

使用OrderByDescendingelse情况下,效果会更好。
充满希望的

很棒!不要忘了添加dataGridView.ColumnHeaderMouseClick += dataGridView_ColumnHeaderMouseClick;并为BindingList实现此功能,每次输入数据时都会更新BindingList ...您必须修改代码...dataGridView.DataSource = new BindingList<Data>(dataList.OrderBy(dataGridView.Columns[e.ColumnIndex].DataPropertyName).ToList());
hyukkyulee

6

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

创建此事件处理程序:

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

然后像这样初始化每个datragrid的事件:

        dataGridView1.DataBindingComplete += MakeColumnsSortable_DataBindingComplete;

我喜欢使用DataBindingComplete事件的想法,但这不能解决没有可排序数据集的问题。我对其进行了编辑以使其更加可重用
Stephen Turner

6

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

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

确保您为事件订阅了数据网格ColumnHeaderMouseClick。当用户单击列时,它将按降序排序。如果再次单击相同的列标题,则将按升序应用排序。


不错的解决方案。谢谢。
timanderson

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()
    }
}

我想使用这个想法,但是我该如何管理单击哪个列标题?通过该特定列名称对datagridview顺序进行排序?谢谢
Mahdi Rashidi 2015年

@ mr.dev.eloper-DataGridViewCellMouseEventArgs对象包含“ ColumnIndex”属性。
jjspierx

3


使用实体框架(在这种情况下为版本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;

2

吻:保持简单,愚蠢

方式A:喜欢使用DataBinding和时, 实现自己的SortableBindingListsorting

方式B: 使用List <string>排序也可以,但不能与DataBinding一起使用


1

如果您收到类似的错误消息

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

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

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

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

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 <Student>类,以便进行排序

    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的表单中添加

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

希望这可以帮助


1

我建议使用DataTable.DefaultView作为数据源。然后下面的行。

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

之后,gridview本身将管理排序(支持升序或降序)。


1

将此行放入Windows窗体中(在加载时或在诸如“ binddata”之类的公共方法中更好):

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

0

以防万一有人仍在寻找它,我在VS 2008 C#上做到了。

在事件ColumnHeaderMouseClick上,为gridview添加数据绑定,然后像参数一样按字段发送订单。您可以按以下方式获得单击字段:

dgView.Columns[e.ColumnIndex].Name

在我的情况下,标题的名称类似于视图字段的名称。


0

我有一个BindingList <>对象绑定作为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

然后对我的代码进行此更改:

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;

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


0

就我而言,问题是我将my设置DataSourceobject,这就是为什么它没有被排序的原因。从更改object为a后DataTable,无需任何代码补充即可很好地工作。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.