[ACCEPTED]-C# DataGridView sorting with Generic List as underlying source-datagridview

Accepted answer
Score: 32

Complete code to sort the column of datagridview 1 whose datasource is a generic 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;
        }
    }
Score: 10

I find it hard to believe the grid doesn't 23 provide basic sorting out of the box, no 22 code needed. After all, it is pretty silly 21 to have to handle a header click event and 20 call DataGridView.Sort indicating the column 19 (determined by what was clicked, tracked 18 by the grid) and the sort direction (determined 17 by current sort state, tracked by the grid).

Why 16 isn't there simply a SortMode or an AllowUserToSort 15 property that does exactly the same thing 14 by default?

I've bound my grid to a List 13 and the properties I've mapped columns to 12 are all basic types like string, int, DateTime 11 and so on. All of which are IComparable. So 10 why on earth should I need to write even 9 one line of code? Especially considering 8 that the documentation reads:

By default, users 7 can sort the data in a DataGridView control 6 by clicking the header of a text box column.

MSDN

That's 5 the Framework 3.0 doc, and I'm targeting 4 3.5, but "other versions" all 3 refer to versions of Visual Studio, not 2 versions of the Framework. What on earth 1 is going on here Microsoft?!?

Score: 6

A good Solution in this article "Presenting 1 the SortableBindingList": http://www.timvw.be/2007/02/22/presenting-the-sortablebindinglistt/

Score: 3

Here is a simpler solution to sort by column 3 using Reflection and Linq. dataGridView1's 2 DataSource is set to compareList which is 1 declared as:

    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; }
    }
}
Score: 1

My Solution is this:

I work with myBindingSource 6 at my own, I do sorting, grouping ..whatever 5 in a separate thread. Then I simply bind the result to the DataGridView.

myDataGridView.DataSource = bindingSource;

For 4 this purpose I've setted all the columns 3 to be sorted 'Programatically' (in designer) Then I manually 2 add the arrow (ASCENDING / DESCENDING) by 1 setting

cell.SortGlyphDirection = ... ; 

in code behind.

Score: 1

If creating your own user control is preferable, you 3 can make a custom sort method using the 2 code below:

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

You can then override the header 1 click method to call your sort function:

    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);
    }
Score: 0

see this artice

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

by reading it i saw this 7 "This method sorts the contents of 6 the DataGridView by comparing values in 5 the specified column. By default, the sort 4 operation will use the Compare method to 3 compare pairs of cells in the column using 2 the DataGridViewCell..::.Value property."

Best 1 Regards, iordan

Score: 0

You may also want to take a look to this 2 post where you can get two interesting links 1 to implement a customized SortableBindingList:

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

Score: 0

Another option to solve the sorting issue 12 with DataGridView when binding to List is, if 11 you are not dealing with huge data set then 10 probably you can attempt to convert the 9 List to DataTable, and then bind the resulting 8 DataTable to the BindingSource/DataGridView.

This 7 would the need for a custom implementation 6 of IComparer. In my case, I was dealing 5 with a smaller list but there were more 4 fields to display. So implementing IComparer 3 meant writing too much boiler plate code.

Check 2 this for terse way of converting the List 1 to DataTable: https://stackoverflow.com/a/34062898/4534493

Score: 0

First of all I used System.Reflection; then: write 1 this method

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

then call method : DataTable dt = ToDataTable(lst.ToList());

Score: 0

Universal sorting of DataGridView columns 1 with DataSource

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

More Related questions