using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Linq; using System.Linq.Expressions; namespace Developpez.Dotnet.Collections { /// /// Représente une liste supportant les scénarios de liaison de données /// ainsi que le tri /// /// Type des éléments de la liste public class SortableBindingList : BindingList { #region Static sort implementation private static readonly Dictionary> _comparisons; static SortableBindingList() { _comparisons = new Dictionary>(); } private static Comparison GetComparison(PropertyDescriptor prop, ListSortDirection direction) { Comparison comparison; lock (_comparisons) { if (!_comparisons.TryGetValue(prop, out comparison)) { comparison = CreateComparison(prop); _comparisons[prop] = comparison; } } if (direction == ListSortDirection.Descending) comparison = comparison.Reverse(); return comparison; } private static Comparison CreateComparison(PropertyDescriptor prop) { var a = Expression.Parameter(typeof (T), "a"); var b = Expression.Parameter(typeof(T), "b"); var comparerType = typeof (Comparer<>).MakeGenericType(prop.PropertyType); var comparerProperty = comparerType.GetProperty("Default"); var compareMethod = comparerType.GetMethod("Compare"); var comparer = Expression.Property(null, comparerProperty); var body = Expression.Call( comparer, compareMethod, Expression.Property(a, prop.Name), Expression.Property(b, prop.Name)); var lambda = Expression.Lambda>(body, a, b); return lambda.Compile(); } #endregion #region Private data private bool _isSorted; private PropertyDescriptor _sortProperty; private ListSortDirection _sortDirection; private List _unsortedList; #endregion #region Constructors /// /// Initialise une nouvelle instance de . /// public SortableBindingList() { } /// /// Initialise une nouvelle instance de avec la liste spécifiée. /// /// La liste d'éléments à contenir dans la public SortableBindingList(IList list) : base(list) { } #endregion #region BindingList overrides /// /// Obtient une valeur indiquant si la liste supporte le tri. /// protected override bool SupportsSortingCore { get { return true; } } /// /// Trie les éléments de la liste selon la propriété et la direction spécifiées /// /// La propriété selon laquelle trier la liste /// La direction selon laquelle trier la liste protected override void ApplySortCore(PropertyDescriptor prop, ListSortDirection direction) { Debug.Print("ApplySortCore({0}, {1})", prop.Name, direction); bool raiseEvents = RaiseListChangedEvents; try { RaiseListChangedEvents = false; var tmpList = Items.ToList(); Comparison comparison = GetComparison(prop, direction); tmpList.Sort(comparison); if (_unsortedList == null) _unsortedList = Items.ToList(); Items.Clear(); foreach (var item in tmpList) { Items.Add(item); } _isSorted = true; _sortProperty = prop; _sortDirection = direction; } finally { RaiseListChangedEvents = raiseEvents; } OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1)); } /// /// Obtient une valeur indiquant si la liste est triée /// protected override bool IsSortedCore { get { return _isSorted; } } /// /// Retire le tri appliqué avec ApplySortCore. /// protected override void RemoveSortCore() { Debug.Print("RemoveSortCore()"); bool raiseEvents = RaiseListChangedEvents; try { RaiseListChangedEvents = false; if (_unsortedList != null) { Items.Clear(); foreach (var item in _unsortedList) { Items.Add(item); } } _isSorted = false; _sortProperty = null; _unsortedList = null; } finally { RaiseListChangedEvents = raiseEvents; } OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1)); } /// /// Obtient la direction du tri si la liste est triée /// protected override ListSortDirection SortDirectionCore { get { return _sortDirection; } } /// /// Obtient la propriété selon laquelle la liste est triée /// protected override PropertyDescriptor SortPropertyCore { get { return _sortProperty; } } /// /// Insère l'élément spécifié dans la liste à la position spécifiée /// /// Position à laquelle insérer l'élément /// Élément à insérer protected override void InsertItem(int index, T item) { Debug.Print("InsertItem({0}, {1})", index, item); base.InsertItem(index, item); if (_unsortedList != null) { _unsortedList.Add(item); } } /// /// Retire de la liste l'élément qui se trouve à la position spécifiée /// /// Position de l'élément à retirer protected override void RemoveItem(int index) { Debug.Print("RemoveItem({0})", index); T item = this[index]; base.RemoveItem(index); if (_unsortedList != null) { _unsortedList.Remove(item); } } /// /// Remplace l'élément à la position spécifiée par l'élément spécifié /// /// Position de l'élément à remplacer /// Nouvel élément à placer à cette position protected override void SetItem(int index, T item) { Debug.Print("SetItem({0}, {1})", index, item); T oldItem = this[index]; base.SetItem(index, item); if (_unsortedList != null) { int idx = _unsortedList.IndexOf(oldItem); if (idx >= 0) _unsortedList[idx] = item; } } /// /// Retire tous les éléments de la liste /// protected override void ClearItems() { Debug.Print("ClearItems()"); base.ClearItems(); if (_unsortedList != null) _unsortedList.Clear(); } #endregion } }