Eric Anastas
Eric Anastas

Reputation: 22213

Reverse sort a BindingList<T>

I have a custom class that inherits from BindingList(T) that I am binding to a DataGrid.

However, the DataGrid is being populated from the top down and I want it populated from the bottom up. So the bottom item is index 0 rather then the top item.

How can I change my BindingList(T) so that the DataGrid reads it in reverse?

Upvotes: 0

Views: 2409

Answers (4)

TheVillageIdiot
TheVillageIdiot

Reputation: 40507

This article on CodeProject.com about implementing sortable BindingList might help you.

It has a nice generic wrapper for binding list that makes it sortable:

public class MySortableBindingList<T> : BindingList<T> {

    // reference to the list provided at the time of instantiation
    List<T> originalList;
    ListSortDirection sortDirection;
    PropertyDescriptor sortProperty;

    // function that refereshes the contents
    // of the base classes collection of elements
    Action<MySortableBindingList<T>, List<T>> 
                   populateBaseList = (a, b) => a.ResetItems(b);

    // a cache of functions that perform the sorting
    // for a given type, property, and sort direction
    static Dictionary<string, Func<List<T>, IEnumerable<T>>> 
       cachedOrderByExpressions = new Dictionary<string, Func<List<T>, 
                                                 IEnumerable<T>>>();

    public MySortableBindingList() {
        originalList = new List<T>();
    }

    public MySortableBindingList(IEnumerable<T> enumerable) {
        originalList = enumerable.ToList();
        populateBaseList(this, originalList);
    }

    public MySortableBindingList(List<T> list) {
        originalList = list;
        populateBaseList(this, originalList);
    }

    protected override void ApplySortCore(PropertyDescriptor prop, 
                            ListSortDirection direction) {
        /*
         Look for an appropriate sort method in the cache if not found .
         Call CreateOrderByMethod to create one. 
         Apply it to the original list.
         Notify any bound controls that the sort has been applied.
         */

        sortProperty = prop;

        var orderByMethodName = sortDirection == 
            ListSortDirection.Ascending ? "OrderBy" : "OrderByDescending";
        var cacheKey = typeof(T).GUID + prop.Name + orderByMethodName;

        if (!cachedOrderByExpressions.ContainsKey(cacheKey)) {
            CreateOrderByMethod(prop, orderByMethodName, cacheKey);
        }

        ResetItems(cachedOrderByExpressions[cacheKey](originalList).ToList());
        ResetBindings();
        sortDirection = sortDirection == ListSortDirection.Ascending ? 
                        ListSortDirection.Descending : ListSortDirection.Ascending;
    }


    private void CreateOrderByMethod(PropertyDescriptor prop, 
                 string orderByMethodName, string cacheKey) {

        /*
         Create a generic method implementation for IEnumerable<T>.
         Cache it.
        */

        var sourceParameter = Expression.Parameter(typeof(List<T>), "source");
        var lambdaParameter = Expression.Parameter(typeof(T), "lambdaParameter");
        var accesedMember = typeof(T).GetProperty(prop.Name);
        var propertySelectorLambda =
            Expression.Lambda(Expression.MakeMemberAccess(lambdaParameter, 
                              accesedMember), lambdaParameter);
        var orderByMethod = typeof(Enumerable).GetMethods()
                                      .Where(a => a.Name == orderByMethodName &&
                                                   a.GetParameters().Length == 2)
                                      .Single()
                                      .MakeGenericMethod(typeof(T), prop.PropertyType);

        var orderByExpression = Expression.Lambda<Func<List<T>, IEnumerable<T>>>(
                                    Expression.Call(orderByMethod,
                                            new Expression[] { sourceParameter, 
                                                               propertySelectorLambda }),
                                            sourceParameter);

        cachedOrderByExpressions.Add(cacheKey, orderByExpression.Compile());
    }

    protected override void RemoveSortCore() {
        ResetItems(originalList);
    }

    private void ResetItems(List<T> items) {

        base.ClearItems();

        for (int i = 0; i < items.Count; i++) {
            base.InsertItem(i, items[i]);
        }
    }

    protected override bool SupportsSortingCore {
        get {
            // indeed we do
            return true;
        }
    }

    protected override ListSortDirection SortDirectionCore {
        get {
            return sortDirection;
        }
    }

    protected override PropertyDescriptor SortPropertyCore {
        get {
            return sortProperty;
        }
    }

    protected override void OnListChanged(ListChangedEventArgs e) {
        originalList = base.Items.ToList();
    }
}

Upvotes: 2

Gergely Orosz
Gergely Orosz

Reputation: 6485

If your list is inheriting from a Collection class, you can use the Insert(0,item) method to insert to the beginning of the list. That way the newest element would appear on the top. Not sure if the BindingList supports this though.

Upvotes: 0

casperOne
casperOne

Reputation: 74530

Why not call the ApplySort method on the IBindingList interface implementation on the BindingList<T>? You can just pass the value of ListSortDirection.Descending for the second parameter and the DataGrid should display the items in reverse order.

Upvotes: 0

Nader Shirazie
Nader Shirazie

Reputation: 10776

Its simple, but you could also populate the binding list in reverse order (unless you have another reason for keeping the current order).

Upvotes: 0

Related Questions