3

On several recommendations, I started to assign a DataSource to my DataGridView instead of using DataGridView.Rows.Add(...). This is convenient since my data source is already a big list which doesn't change (much). However, when I use the DataSource assignment, it becomes impossible to sort the columns.

class MyGridView : DataGridView
{
    private List<Person> m_personList;

    private class Person
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public Person(string first, string last)
        {
            FirstName = first;
            LastName = last;
        }
    }

    public MyGridView()
    {
         /* ...initialise stuff... */
         m_personList.Add(new Person("Kate", "Smith"));
         m_personList.Add(new Person("Bill", "Davids"));
         m_personList.Add(new Person("Ann", "Roth"));

         this.DataSource = m_personList; 
    }
}

I also tried to replace List<Person> by BindingList<Person> and by BindingSource, but none of that seems to matter. I also tried adding a custom sorter:

this.SortCompare += MyGridView_SortCompare;

private void MyGridView_SortCompare(object sender, EventArgs e)
{
    /* ...Compare method...*/
}

but the thing doesn't even get called. Is there some other way to enable sorting with a DataSource?

Note: Note that my DataSource is not (necessarily) an SQL one, but just any List.

Yellow
  • 3,559
  • 6
  • 35
  • 66
  • Is this what you're looking for http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.gridview.sort.aspx ? – Rameez Ahmed Sayad Oct 01 '13 at 09:35
  • I'm not quite sure, but I don't think it is. What I want is merely that clicking on a column header sorts everything, just as when you'd add everything to the rows without using DataSource. I'm not really looking to implement a complicated Sort method. – Yellow Oct 01 '13 at 09:40
  • The above link is quite simple rite `MyGridView.Sort("ColumnName", SortDirection.Ascending);` , put that inside `RowDataBound` or wherever you add a new row. – Rameez Ahmed Sayad Oct 01 '13 at 10:14
  • But how do I make it so that clicking on a column header actually calls that method, for the correct column? – Yellow Oct 01 '13 at 10:19
  • 1
    That link is for the -web- GridView not the Winforms DataGridView – stuartd Oct 01 '13 at 10:31
  • possible duplicate of [C# DataGridView sorting with Generic List as underlying source](http://stackoverflow.com/questions/1377568/c-sharp-datagridview-sorting-with-generic-list-as-underlying-source) – Yellow Oct 01 '13 at 10:56

2 Answers2

9

You need to coerce your datasource into a list that supports sorting (IBindingList and IBindingListView), and then it will work out of the box. There are lots of examples on the web, this is the one I've used in the past:

// usage:
// var sortableList = new SortableList(m_personList);
// dgv.DataSource = m_sortableList; 


/// <summary>
///  Suitable for binding to DataGridView when column sorting is required
/// </summary>
/// <typeparam name="T"></typeparam>
public class SortableList<T> : BindingList<T>, IBindingListView
{
    private PropertyComparerCollection<T> sorts;

    public SortableList()
    {
    }

    public SortableList(IEnumerable<T> initialList)
    {
        foreach (T item in initialList)
        {
            this.Add(item);
        }
    }

    public SortableList<T> ApplyFilter(Func<T, bool> func)
    {
        SortableList<T> newList = new SortableList<T>();
        foreach (var item in this.Where(func))
        {
            newList.Add(item);
        }

        return newList;
    }

    protected override bool IsSortedCore
    {
        get { return this.sorts != null; }
    }

    protected override bool SupportsSortingCore
    {
        get { return true; }
    }

    protected override ListSortDirection SortDirectionCore
    {
        get
        {
            return this.sorts == null
                       ? ListSortDirection.Ascending
                       : this.sorts.PrimaryDirection;
        }
    }

    protected override PropertyDescriptor SortPropertyCore
    {
        get
        {
            return this.sorts == null ? null : this.sorts.PrimaryProperty;
        }
    }

    public void ApplySort(ListSortDescriptionCollection
                              sortCollection)
    {
        bool oldRaise = RaiseListChangedEvents;
        RaiseListChangedEvents = false;
        try
        {
            PropertyComparerCollection<T> tmp
                = new PropertyComparerCollection<T>(sortCollection);
            List<T> items = new List<T>(this);
            items.Sort(tmp);
            int index = 0;
            foreach (T item in items)
            {
                SetItem(index++, item);
            }
            this.sorts = tmp;
        }
        finally
        {
            RaiseListChangedEvents = oldRaise;
            ResetBindings();
        }
    }

    public bool Exists(Predicate<T> func)
    {
        return new List<T>(this).Exists(func);
    }

    string IBindingListView.Filter
    {
        get { throw new NotImplementedException(); }
        set { throw new NotImplementedException(); }
    }

    void IBindingListView.RemoveFilter()
    {
        throw new NotImplementedException();
    }

    ListSortDescriptionCollection IBindingListView.SortDescriptions
    {
        get { return (this.sorts == null ? null : this.sorts.Sorts); }
    }

    bool IBindingListView.SupportsAdvancedSorting
    {
        get { return true; }
    }

    bool IBindingListView.SupportsFiltering
    {
        get { return false; }
    }

    protected override void RemoveSortCore()
    {
        this.sorts = null;
    }

    protected override void ApplySortCore(PropertyDescriptor prop, ListSortDirection direction)
    {
        ListSortDescription[] arr = { new ListSortDescription(prop, direction) };
        ApplySort(new ListSortDescriptionCollection(arr));
    }
}

public class PropertyComparerCollection<T> : IComparer<T>
{
    private readonly PropertyComparer<T>[] comparers;

    private readonly ListSortDescriptionCollection sorts;

    public PropertyComparerCollection(ListSortDescriptionCollection
                                          sorts)
    {
        if (sorts == null)
        {
            throw new ArgumentNullException("sorts");
        }
        this.sorts = sorts;
        List<PropertyComparer<T>> list = new
            List<PropertyComparer<T>>();

        foreach (ListSortDescription item in sorts)
        {
            list.Add(new PropertyComparer<T>(item.PropertyDescriptor,
                                             item.SortDirection == ListSortDirection.Descending));
        }

        this.comparers = list.ToArray();
    }

    public ListSortDescriptionCollection Sorts
    {
        get { return this.sorts; }
    }

    public PropertyDescriptor PrimaryProperty
    {
        get
        {
            return this.comparers.Length == 0
                       ? null
                       : this.comparers[0].Property;
        }
    }

    public ListSortDirection PrimaryDirection
    {
        get
        {
            return this.comparers.Length == 0
                       ? ListSortDirection.Ascending
                       : this.comparers[0].Descending
                             ? ListSortDirection.Descending
                             : ListSortDirection.Ascending;
        }
    }

    int IComparer<T>.Compare(T x, T y)
    {
        int result = 0;
        foreach (PropertyComparer<T> t in this.comparers)
        {
            result = t.Compare(x, y);
            if (result != 0)
            {
                break;
            }
        }
        return result;
    }
}

public class PropertyComparer<T> : IComparer<T>
{
    private readonly bool descending;

    private readonly PropertyDescriptor property;

    public PropertyComparer(PropertyDescriptor property, bool descending)
    {
        if (property == null)
        {
            throw new ArgumentNullException("property");
        }

        this.descending = descending;
        this.property = property;
    }

    public bool Descending
    {
        get { return this.descending; }
    }

    public PropertyDescriptor Property
    {
        get { return this.property; }
    }

    public int Compare(T x, T y)
    {
        int value = Comparer.Default.Compare(this.property.GetValue(x),
                                             this.property.GetValue(y));
        return this.descending ? -value : value;
    }
}
stuartd
  • 62,136
  • 13
  • 120
  • 150
  • Ah, thanks, now I know what to look for, I suddenly find the answer everywhere! :) I'll mark my question as duplicate. – Yellow Oct 01 '13 at 10:55
  • What libraries need to be used for this, by the way? `Func` and the `this.Where(func)` function are not recognised, and I can't find out where to get them. – Yellow Oct 01 '13 at 11:03
  • Does it allow for sorting multiple columns at once? – Bernhard Döbler Apr 23 '16 at 17:59
1

An alternate is to convert your List into DataTable and then bind that DataTable to the DataGridView through BindingSource. This way your DataGridView will inherit the sort options available with the DataTable.

For converting a list to DataTable, refer the article: How to fill a datatable with List<T>

Community
  • 1
  • 1
Sudeep
  • 41
  • 1
  • 3