user3070641
user3070641

Reputation: 33

C# - troubles with sorting datagridview

I have searched my ass off for the answer of my problem. I have developed a c# Winforms program with a few datagridviews.

The problem is that I want to be the user able to sort the datagridview by clicking on the columnheader (i thought this would be standard...) But it is just not working.

I tried the dgv.Sort-method but this throws an exception that the datagridview has to be bound to an IBindingList but I have no clue how to do that and I really do not want to redevelope everything..

Here's how I populate my dgv.

I have certain custom objects and put them into a list. When this list is fullypopulated I set it as the datasource for the dgv.

list.Add(costumobject);
.
.
.
dgv.DataSource = list;

Could you please tell me a quick way to make the sort-function working?

Kind regards,

Upvotes: 2

Views: 3836

Answers (3)

mjhillman
mjhillman

Reputation: 59

DataGridView does not internally sort its rows itself. Instead, it exploits the sorting functionality of the attached data source, and if the data source does not provide sorting functionality, DataGridView column sorting is not available also.

Create a SortableBindingList and use that instead of a List.

using System;
using System.Collections.Generic;
using System.ComponentModel;

namespace YourNamespace
{
    /// <summary>
    /// Provides a generic collection that supports data binding and additionally supports sorting.
    /// See http://msdn.microsoft.com/en-us/library/ms993236.aspx
    /// If the elements are IComparable it uses that; otherwise compares the ToString()
    /// </summary>
    /// <typeparam name="T">The type of elements in the list.</typeparam>
    public class SortableBindingList<T> : BindingList<T> where T : class
    {
        private bool _isSorted;
        private ListSortDirection _sortDirection = ListSortDirection.Ascending;
        private PropertyDescriptor _sortProperty;

        /// <summary>
        /// Initializes a new instance of the <see cref="SortableBindingList{T}"/> class.
        /// </summary>
        public SortableBindingList()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="SortableBindingList{T}"/> class.
        /// </summary>
        /// <param name="list">An <see cref="T:System.Collections.Generic.IList`1" /> of items to be contained in the <see cref="T:System.ComponentModel.BindingList`1" />.</param>
        public SortableBindingList(IList<T> list) : base(list)
        {
        }

        /// <summary>
        /// Gets a value indicating whether the list supports sorting.
        /// </summary>
        protected override bool SupportsSortingCore
        {
            get { return true; }
        }

        /// <summary>
        /// Gets a value indicating whether the list is sorted.
        /// </summary>
        protected override bool IsSortedCore
        {
            get { return _isSorted; }
        }

        /// <summary>
        /// Gets the direction the list is sorted.
        /// </summary>
        protected override ListSortDirection SortDirectionCore
        {
            get { return _sortDirection; }
        }

        /// <summary>
        /// Gets the property descriptor that is used for sorting the list if sorting is implemented in a derived class; otherwise, returns null
        /// </summary>
        protected override PropertyDescriptor SortPropertyCore
        {
            get { return _sortProperty; }
        }

        /// <summary>
        /// Removes any sort applied with ApplySortCore if sorting is implemented
        /// </summary>
        protected override void RemoveSortCore()
        {
            _sortDirection = ListSortDirection.Ascending;
            _sortProperty = null;
            _isSorted = false; //thanks Luca
        }

        /// <summary>
        /// Sorts the items if overridden in a derived class
        /// </summary>
        /// <param name="prop"></param>
        /// <param name="direction"></param>
        protected override void ApplySortCore(PropertyDescriptor prop, ListSortDirection direction)
        {
            _sortProperty = prop;
            _sortDirection = direction;

            List<T> list = Items as List<T>;
            if (list == null) return;
            list.Sort(Compare);
            _isSorted = true;
            //fire an event that the list has been changed.
            OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
        }

        private int Compare(T lhs, T rhs)
        {
            var result = OnComparison(lhs, rhs);
            //invert if descending
            if (_sortDirection == ListSortDirection.Descending)
                result = -result;
            return result;
        }

        private int OnComparison(T lhs, T rhs)
        {
            object lhsValue = lhs == null ? null : _sortProperty.GetValue(lhs);
            object rhsValue = rhs == null ? null : _sortProperty.GetValue(rhs);
            if (lhsValue == null)
            {
                return (rhsValue == null) ? 0 : -1; //nulls are equal
            }

            if (rhsValue == null)
            {
                return 1; //first has value, second doesn't
            }

            if (lhsValue is IComparable)
            {
                return ((IComparable)lhsValue).CompareTo(rhsValue);
            }

            if (lhsValue.Equals(rhsValue))
            {
                return 0; //both are the same
            }

            //not comparable, compare ToString
            return lhsValue.ToString().CompareTo(rhsValue.ToString());
        }
    }
}

Upvotes: 4

TaW
TaW

Reputation: 54433

List<T> doesn't support sorting directly.

Instead you can use a Linq routine to do the sorting.

However you will need to include a sort field check, which will be as long as your number of columns..

Not knowing your customobject class let's try with a Name class:

class Name
{
    public string first { get; set; }
    public string last { get; set; }
    public string middle { get; set; }

    public Name (string f, string m, string l)
    {
        first = f; middle = m; last = l;
    }
}

Now let's code the ColumnHeaderMouseClick event:

private void dataGridView1_ColumnHeaderMouseClick(object sender, 
                           DataGridViewCellMouseEventArgs e)
{
    List<Name> names = dataGridView1.DataSource as List<Name>;
    string col = dataGridView2.Columns[e.ColumnIndex].DataPropertyName;
    string order =  " ASC";
    if (dataGridView1.Tag != null) 
        order = dataGridView1.Tag.ToString().Contains(" ASC") ? " DESC" : " ASC";

    dataGridView1.Tag = col + order;

    if (order.Contains(" ASC"))
    names = names.OrderBy(x => col == "first"? x.first 
                             : col == "last" ? x.last : x.middle).ToList();  
    else
    names = names.OrderByDescending(x => col == "first"? x.first : 
                                         col == "last" ? x.last : x.middle).ToList();  

    dataGridView1.DataSource = names;
}

Note that I store the current sort column and order in the DGV's Tag. You can move it to a class level variable or some other place. Unfortunately the DGV's SortOrder property can't be set..

Upvotes: 1

mikeliezel1102
mikeliezel1102

Reputation: 1

have you tried to refresh the datagrid after changing datasource?

list.Add(costumobject);
.
.
.
dgv.DataSource = list;
dgv.Refresh();

Upvotes: 0

Related Questions