使用泛型列表作为底层数据源的C# DataGridView排序

23

我正在使用一个 Windows Forms DataGridView 来显示一个 MyObject 对象的通用列表。

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

dataGridView.DataSource = myBindingSource;

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

我阅读了一些文章,其中提到应在绑定数据之前进行排序。但是如果我想实时对已经绑定的数据进行排序,这并没有帮助我。

问题是,我需要做什么才能在DataGridView中看到排序箭头并对每一列进行排序?


1
请参考 https://dev59.com/Km865IYBdhLWcg3whfBf。使用 http://sourceforge.net/projects/blw/ 中的库,只需一行代码 dataGridView.DataSource = new BindingListView<MyObject>(myObjects.ToList()) 即可实现自动排序。 - Pat
11个回答

32

完整的代码来对数据源为泛型列表的DataGridView列进行排序

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

非常好的解决方案,非常感谢!不过我花了一些时间对其进行了改进。 - Nikola Malešević
如果我尝试点击年龄列,会出现以下情况:System.ArgumentException: '对象必须是字符串类型。' - Binuriki Cliean Jay

10

我很难相信网格控件没有基本的排序功能,而无需代码。毕竟,必须处理头部点击事件并调用DataGridView.Sort指示列(由网格跟踪)和排序方向(由当前排序状态跟踪)是相当愚蠢的。

为什么不简单地提供一个SortMode或AllowUserToSort属性,以默认方式执行完全相同的操作呢?

我已将我的网格绑定到List,并且我映射到列的属性都是基本类型,例如string、int、DateTime等。所有这些类型都是可比较的。那么我为什么需要编写任何代码呢?特别是考虑到文档中的说明:

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

MSDN

这是Framework 3.0文档,而我正在针对3.5版本开发,但“其他版本”都是指Visual Studio的版本,而不是Framework的版本。Microsoft究竟在搞什么?!?


顺便提一下,我的列都是DataGridViewTextBoxColumn类型的 - 这可能就是微软所说的“文本框列”。 - The Dag
有一个SortMode但它似乎没有任何作用! - ProfK

6

3

这里有一种使用反射和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; }
    }
}

1
我稍微修改了这个解决方案以获得更快的结果。我使用了 DataPropertyName 而不是 Name 作为 strColumnName,而且我在 click 事件中将我的 compareList 强制转换为 List<myobjecttype>:var compareList = (dataGridView1.DataSource as List<myobjecttype>)。对于我一次性的应用来说,这已经足够好了。 - archangel76

1

我的解决方案是这样的:

我在自己的线程中使用myBindingSource,进行排序、分组等操作。然后我简单地将结果绑定到DataGridView上

myDataGridView.DataSource = bindingSource;

为此,我已将所有列设置为“程序化”排序(在设计器中)。然后我手动添加箭头(升序/降序)通过设置


cell.SortGlyphDirection = ... ; 

在代码后台。


1
如果您希望创建自己的用户控件,则可以使用以下代码创建自定义排序方法:
    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);
    }

0
首先,我使用了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());


0

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

0

请查看这篇文章

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

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

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接