使用通用列表作为基础源的 C# DataGridView 排序

Posted

技术标签:

【中文标题】使用通用列表作为基础源的 C# DataGridView 排序【英文标题】:C# DataGridView sorting with Generic List as underlying source 【发布时间】:2009-09-04 06:34:09 【问题描述】:

我正在使用 Windows 窗体 DataGridView 来显示 MyObject 对象的通用列表。

首先我把这个集合包装成一个BindingSource集合,然后:

dataGridView.DataSource = myBindingSource;

我想要做的是允许用户通过单击代表 MyObject 中的具体属性的列的标题来对列进行排序。

我已经阅读了一些我应该在绑定之前进行排序的文章。但是,如果我想实时对列进行排序,这对我没有帮助,因为它已经绑定了。

问题是,我到底需要做什么,所以我可以在 DataGridView 中看到排序箭头,并且我可以对每一列进行排序

【问题讨论】:

见***.com/questions/3770857/…。就像dataGridView.DataSource = new BindingListView<MyObject>(myObjects.ToList()) 一样简单,库位于sourceforge.net/projects/blw。 【参考方案1】:

对datagridview的数据源为泛型List的列进行排序的完整代码

//-----------------------------------------------------------------------------------------
//In the form - In constructor or form load, populate the grid.
//--------------------------------------------------------------------------------------------

    List<student> students;

    private void PopulateList()
    
        student std1 = new student("sss", 15, "Female");
        student std2 = new student("ddd", 12, "Male");
        student std3 = new student("zzz", 16, "Male");
        student std4 = new student("qqq", 14, "Female");
        student std5 = new student("aaa", 11, "Male");
        student std6 = new student("lll", 13, "Female");

        students = new List<student>();
        students.Add(std1);
        students.Add(std2);
        students.Add(std3);
        students.Add(std4);
        students.Add(std5);
        students.Add(std6);

        dataGridView1.DataSource = students;
    


//---------------------------------------------------------------------------------------------
//Comparer class to perform sorting based on column name and sort order
//---------------------------------------------------------------------------------------------


class StudentComparer : IComparer<Student>

    string memberName = string.Empty; // specifies the member name to be sorted
    SortOrder sortOrder = SortOrder.None; // Specifies the SortOrder.

    /// <summary>
    /// constructor to set the sort column and sort order.
    /// </summary>
    /// <param name="strMemberName"></param>
    /// <param name="sortingOrder"></param>
    public StudentComparer(string strMemberName, SortOrder sortingOrder)
    
        memberName = strMemberName;
        sortOrder = sortingOrder;
    

    /// <summary>
    /// Compares two Students based on member name and sort order
    /// and return the result.
    /// </summary>
    /// <param name="Student1"></param>
    /// <param name="Student2"></param>
    /// <returns></returns>
    public int Compare(Student Student1, Student Student2)
    
        int returnValue = 1;
        switch (memberName)
        
            case "Name" :
                if (sortOrder == SortOrder.Ascending)
                
                    returnValue = Student1.Name.CompareTo(Student2.Name);
                
                else
                
                    returnValue = Student2.Name.CompareTo(Student1.Name);
                

                break;
            case "Sex":
                if (sortOrder == SortOrder.Ascending)
                
                    returnValue = Student1.Sex.CompareTo(Student2.Sex);
                
                else
                
                    returnValue = Student2.Sex.CompareTo(Student1.Sex);
                
                break;
            default:
                if (sortOrder == SortOrder.Ascending)
                
                    returnValue = Student1.Name.CompareTo(Student2.Name);
                
                else
                
                    returnValue = Student2.Name.CompareTo(Student1.StudentId);
                
                break;
        
        return returnValue;
    




//---------------------------------------------------------------------------------------------
// Performing sort on click on Column Header
//---------------------------------------------------------------------------------------------

    private void dataGridView1_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)
    
        //get the current column details
        string strColumnName = dataGridView1.Columns[e.ColumnIndex].Name;
        SortOrder strSortOrder = getSortOrder(e.ColumnIndex);

        students.Sort(new StudentComparer(strColumnName, strSortOrder));
        dataGridView1.DataSource = null;
        dataGridView1.DataSource = students;
        customizeDataGridView();
        dataGridView1.Columns[e.ColumnIndex].HeaderCell.SortGlyphDirection = strSortOrder;
    

   /// <summary>
    /// Get the current sort order of the column and return it
    /// set the new SortOrder to the columns.
    /// </summary>
    /// <param name="columnIndex"></param>
    /// <returns>SortOrder of the current column</returns>
    private SortOrder getSortOrder(int columnIndex)
    
        if (dataGridView1.Columns[columnIndex].HeaderCell.SortGlyphDirection == SortOrder.None ||
            dataGridView1.Columns[columnIndex].HeaderCell.SortGlyphDirection == SortOrder.Descending)
        
            dataGridView1.Columns[columnIndex].HeaderCell.SortGlyphDirection = SortOrder.Ascending;
            return SortOrder.Ascending;
        
        else
        
            dataGridView1.Columns[columnIndex].HeaderCell.SortGlyphDirection = SortOrder.Descending;
            return SortOrder.Descending;
        
    

【讨论】:

很好的解决方案,非常感谢!不过,我花时间对其进行了一些改进。【参考方案2】:

我很难相信网格不提供开箱即用的基本排序,不需要代码。毕竟,必须处理标题单击事件并调用 DataGridView.Sort 指示列(由单击的内容确定,由网格跟踪)和排序方向(由当前的排序状态确定,由网格跟踪)是非常愚蠢的)。

为什么没有简单的 SortMode 或 AllowUserToSort 属性在默认情况下执行完全相同的操作?

我已将我的网格绑定到一个列表,并且我已将列映射到的属性都是基本类型,如字符串、int、DateTime 等。所有这些都是 IComparable。那么到底为什么我需要写一行代码呢?特别是考虑到文档如下:

默认情况下,用户可以对数据进行排序 通过单击 DataGridView 控件 文本框列的标题。

MSDN

那是 Framework 3.0 文档,我的目标是 3.5,但“其他版本”都是指 Visual Studio 的版本,而不是 Framework 的版本。微软到底是怎么回事?!?

【讨论】:

顺便说一句,我的列都是 DataGridViewTextBoxColumn 类型 - 这大概是 MS 在说“文本框列”时的意思。 有一个 SortMode 但它似乎什么也没做!【参考方案3】:

这篇文章“Presenting the SortableBindingList”中的一个很好的解决方案: http://www.timvw.be/2007/02/22/presenting-the-sortablebindinglistt/

【讨论】:

【参考方案4】:

这是一个使用 Reflection 和 Linq 按列排序的更简单的解决方案。 dataGridView1 的 DataSource 设置为 compareList,声明为:

    private List<CompareInfo> compareList;


    private void dataGridView1_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)
    
        string strColumnName = dataGridView1.Columns[e.ColumnIndex].Name;
        SortOrder strSortOrder = getSortOrder(e.ColumnIndex);

        if (strSortOrder == SortOrder.Ascending)
        
            compareList = compareList.OrderBy(x => typeof(CompareInfo).GetProperty(strColumnName).GetValue(x, null)).ToList();
        
        else
        
            compareList = compareList.OrderByDescending(x => typeof(CompareInfo).GetProperty(strColumnName).GetValue(x, null)).ToList();
        
        dataGridView1.DataSource = compareList;
        dataGridView1.Columns[e.ColumnIndex].HeaderCell.SortGlyphDirection = strSortOrder;
    

    private SortOrder getSortOrder(int columnIndex)
    
        if (dataGridView1.Columns[columnIndex].HeaderCell.SortGlyphDirection == SortOrder.None ||
            dataGridView1.Columns[columnIndex].HeaderCell.SortGlyphDirection == SortOrder.Descending)
        
            dataGridView1.Columns[columnIndex].HeaderCell.SortGlyphDirection = SortOrder.Ascending;
            return SortOrder.Ascending;
        
        else
        
            dataGridView1.Columns[columnIndex].HeaderCell.SortGlyphDirection = SortOrder.Descending;
            return SortOrder.Descending;
        
    


public class CompareInfo

    public string FileName  get; set; 

    public string UAT_Folder  get; set; 

    public string UAT_Path
    
        get  return UAT_Folder + FileName; 
    

    public string PROD_Folder  get; set; 

    public string PROD_Path
    
        get  return PROD_Folder + FileName; 
    

【讨论】:

我稍微修改了这个解决方案以获得更快的结果。我使用 DataPropertyName 而不是 Name 作为 strColumnName,并将 compareList 投射到顶部的 click 事件中: var compareList = (dataGridView1.DataSource as List);这足以满足我对一次性应用程序的需求。【参考方案5】:

我的解决方案是这样的:

我自己使用 myBindingSource,我在单独的线程中进行排序、分组..任何事情。 然后我只是将结果绑定到 DataGridView

myDataGridView.DataSource = bindingSource;

为此,我已将所有列设置为排序'Programatically'(在设计器中) 然后我手动添加箭头(ASCENDING / DESCENDING) 通过设置

cell.SortGlyphDirection = ... ; 

在后面的代码中。

【讨论】:

【参考方案6】:

如果创建自己的用户控件更可取,您可以使用以下代码创建自定义排序方法:

    private string _lastSortColumn;
    private ListSortDirection _lastSortDirection;

    public void Sort(DataGridViewColumn column)
    
        // Flip sort direction, if the column chosen was the same as last time
        if (column.Name == _lastSortColumn)
            _lastSortDirection = 1 - _lastSortDirection;
        // Otherwise, reset the sort direction to its default, ascending
        else
        
            _lastSortColumn = column.Name;
            _lastSortDirection = ListSortDirection.Ascending;
        

        // Prep data for sorting
        var data = (IEnumerable<dynamic>)DataSource;
        var orderProperty = column.DataPropertyName;

        // Sort data
        if (_lastSortDirection == ListSortDirection.Ascending)
            DataSource = data.OrderBy(x => x.GetType().GetProperty(orderProperty).GetValue(x, null)).ToList();
        else
            DataSource = data.OrderByDescending(x => x.GetType().GetProperty(orderProperty).GetValue(x, null)).ToList();

        // Set direction of the glyph
        Columns[column.Index].HeaderCell.SortGlyphDirection
            = _lastSortDirection == ListSortDirection.Ascending
            ? SortOrder.Ascending : SortOrder.Descending;
    

然后你可以重写标题点击方法来调用你的排序函数:

    protected override void OnColumnHeaderMouseClick(DataGridViewCellMouseEventArgs e)
    
        base.OnColumnHeaderMouseClick(e);

        var column = Columns[e.ColumnIndex];

        if (column.SortMode == DataGridViewColumnSortMode.Automatic
            || column.SortMode == DataGridViewColumnSortMode.NotSortable)
            Sort(column);
    

【讨论】:

【参考方案7】:

您可能还想看看这篇文章,您可以在其中获得两个有趣的链接来实现自定义的 SortableBindingList:

Sort Datagridview columns when datasource binded to List(Of T)

【讨论】:

【参考方案8】:

看这篇文章

http://msdn.microsoft.com/en-us/library/0868ft3z.aspx

通过阅读它,我看到了“此方法通过比较指定列中的值对 DataGridView 的内容进行排序。默认情况下,排序操作将使用 Compare 方法使用 DataGridViewCell 比较列中的单元格对。 ::.Value 属性。”

最好的问候, 约旦

【讨论】:

【参考方案9】:

在绑定到 List 时解决 DataGridView 排序问题的另一个选项是,如果您不处理庞大的数据集,那么您可能可以尝试将 List 转换为 DataTable,然后将生成的 DataTable 绑定到 BindingSource/DataGridView .

这将需要 IComparer 的自定义实现。就我而言,我正在处理一个较小的列表,但要显示更多字段。所以实现 IComparer 意味着编写太多样板代码。

查看将列表转换为数据表的简洁方法:https://***.com/a/34062898/4534493

【讨论】:

【参考方案10】:

首先我使用了 System.Reflection; 然后:写这个方法

 public DataTable ToDataTable<T>(List<T> items)
    
        DataTable dataTable = new DataTable(typeof(T).Name);
        //Get all the properties
        PropertyInfo[] Props = typeof(T).GetProperties(BindingFlags.Public | 
        BindingFlags.Instance);

        foreach (PropertyInfo prop in Props)
        
            //Setting column names as Property names

            dataTable.Columns.Add(prop.Name);
        
        foreach (T item in items)
        
            var values = new object[Props.Length];

            for (int i = 0; i < Props.Length; i++)
            
                //inserting property values to datatable rows

                values[i] = Props[i].GetValue(item, null);
            
            dataTable.Rows.Add(values);
        
           //put a breakpoint here and check datatable
        return dataTable;
    

然后调用方法: DataTable dt = ToDataTable(lst.ToList());

【讨论】:

【参考方案11】:

使用 DataSource 对 DataGridView 列进行通用排序

private void ItemsTable_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)

    var sortingColumn = ItemsTable.Columns[e.ColumnIndex];

    var propertyInfo = typeof(ListingInfo).GetProperty(sortingColumn.DataPropertyName);
    if (propertyInfo == null) // ignore columns with buttons or pictures
        return;

    foreach (DataGridViewColumn dataGridViewColumn in ItemsTable.Columns)
    
        if (dataGridViewColumn != sortingColumn)
            dataGridViewColumn.HeaderCell.SortGlyphDirection = SortOrder.None;
    
    sortingColumn.HeaderCell.SortGlyphDirection = sortingColumn.HeaderCell.SortGlyphDirection == SortOrder.Ascending
        ? SortOrder.Descending : SortOrder.Ascending;

    _listingsList.Sort((x, y) =>
    
        var first = sortingColumn.HeaderCell.SortGlyphDirection == SortOrder.Ascending ? x : y;
        var second = sortingColumn.HeaderCell.SortGlyphDirection == SortOrder.Ascending ? y : x;
        var firstValue = propertyInfo.GetValue(first);
        var secondValue = propertyInfo.GetValue(second);
        if (firstValue == null)
            return secondValue == null ? 0 : -1;
        return secondValue == null ? 1 : ((IComparable)firstValue).CompareTo(secondValue);
    );

    // tell the binding list to raise a list change event so that bound controls reflect the new item order
    ItemsTable.ResetBindings();
    ItemsTable.Refresh();

【讨论】:

以上是关于使用通用列表作为基础源的 C# DataGridView 排序的主要内容,如果未能解决你的问题,请参考以下文章

c#打印一个由通用列表组成的数组列表

如何使用 json.net 将 c# 通用列表转换为 json?

如何使用 json.net 将 c# 通用列表转换为 json?

c# - 从 SqlDataReader 填充通用列表

C#:将枚举类型作为参数传递

C# - 获取通用列表的项目类型