Click here to Skip to main content
13,194,662 members (51,626 online)
Click here to Skip to main content

Stats

173.3K views
3.6K downloads
83 bookmarked
Posted 1 Dec 2008

Implementing a Sortable BindingList Very, Very Quickly

, 1 Dec 2008
A custom implementation of BindingList that provides sorting for every property of type T.
SortableBindingList
SortableBindingList
Properties
DataSources
SaleDetail.datasource
Sale.datasource
bin
Debug
SortableBindingList.vshost.exe.manifest
SortableBindingList.vshost.exe
SortableBindingList.exe
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Text;

namespace SortableBindingList {

    [DataObject]
    public class Sale {

        public Sale() {
            SaleDate = DateTime.Now;
        }

        public MySortableBindingList<SaleDetail> SaleDetails { get; set; }

        public string Salesman { get; set; }

        public string Client { get; set; }

        public DateTime SaleDate { get; set; }

        public decimal TotalAmount {
            get {
                Debug.Assert(SaleDetails != null);
                return SaleDetails.Sum(a => a.TotalAmount);
            }
        }

    }

    [DataObject]
    public class SaleDetail {

        public string Product { get; set; }

        public int Quantity { get; set; }

        public decimal UnitPrice { get; set; }

        public decimal TotalAmount {
            get {
                return UnitPrice * Quantity;
            }
        }
    }

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

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Muigai Mwaura
Technical Lead Olivine Technology
Kenya Kenya
Technical Lead, Olivine Technology - Nairobi, Kenya.

"The bane of productivity: confusing the rituals of work (sitting at your desk by 8:00am, wearing a clean and well pressed business costume etc.) with actual work that produces results."

Watch me!

You may also be interested in...

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.171018.2 | Last Updated 2 Dec 2008
Article Copyright 2008 by Muigai Mwaura
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid