Last active
April 30, 2025 16:07
-
-
Save jsmarble/a5bf0ededd2df968ea189fea81233472 to your computer and use it in GitHub Desktop.
Custom Sorting of BindingList<T>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class CustomBindingList<T> : BindingList<T> | |
{ | |
private bool isSorting; | |
/// <summary> | |
/// Raised when the list is sorted. | |
/// </summary> | |
public event EventHandler Sorted; | |
public CustomBindingList() | |
: this(null) { } | |
public CustomBindingList(IEnumerable<T> contents) | |
: this(contents, null) { } | |
public CustomBindingList(IEnumerable<T> contents, ISortComparer<T> comparer) | |
{ | |
if (contents != null) | |
AddRange(contents); | |
if (comparer == null) | |
SortComparer = new GenericSortComparer<T>(); | |
else | |
SortComparer = comparer; | |
} | |
#region Properties | |
private ISortComparer<T> sortComparer; | |
public ISortComparer<T> SortComparer | |
{ | |
get { return sortComparer; } | |
set | |
{ | |
if (value == null) | |
throw new ArgumentNullException("SortComparer", "Value cannot be null."); | |
sortComparer = value; | |
} | |
} | |
private bool isSorted; | |
protected override bool IsSortedCore | |
{ | |
get { return isSorted; } | |
} | |
protected override bool SupportsSortingCore | |
{ | |
get { return true; } | |
} | |
private ListSortDirection sortDirection; | |
protected override ListSortDirection SortDirectionCore | |
{ | |
get { return sortDirection; } | |
} | |
private PropertyDescriptor sortProperty; | |
protected override PropertyDescriptor SortPropertyCore | |
{ | |
get { return sortProperty; } | |
} | |
#endregion | |
protected override void ApplySortCore(PropertyDescriptor prop, ListSortDirection direction) | |
{ | |
if (prop == null) | |
return; | |
isSorting = true; | |
sortDirection = direction; | |
sortProperty = prop; | |
this.SortComparer.SortProperty = prop; | |
this.SortComparer.SortDirection = direction; | |
((List<T>)this.Items).Sort(this.SortComparer); | |
isSorted = true; | |
isSorting = false; | |
this.OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, 0)); | |
OnSorted(null, new EventArgs()); | |
} | |
protected override void RemoveSortCore() | |
{ | |
throw new NotSupportedException(); | |
} | |
protected override void OnListChanged(ListChangedEventArgs e) | |
{ | |
if (!isSorting) | |
base.OnListChanged(e); | |
} | |
protected virtual void OnSorted(object sender, EventArgs e) | |
{ | |
if (Sorted != null) | |
Sorted(sender, e); | |
} | |
protected override void InsertItem(int index, T item) | |
{ | |
base.InsertItem(index, item); | |
if (!isSorting) | |
this.ApplySortCore(this.SortPropertyCore, this.SortDirectionCore); | |
} | |
protected override void SetItem(int index, T item) | |
{ | |
base.SetItem(index, item); | |
if (!isSorting) | |
this.ApplySortCore(this.SortPropertyCore, this.SortDirectionCore); | |
} | |
protected override void RemoveItem(int index) | |
{ | |
base.RemoveItem(index); | |
if (!isSorting) | |
this.ApplySortCore(this.SortPropertyCore, this.SortDirectionCore); | |
} | |
protected override void ClearItems() | |
{ | |
base.ClearItems(); | |
} | |
public void AddRange(IEnumerable<T> items) | |
{ | |
if (items != null) | |
foreach (T item in items) | |
this.Items.Add(item); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class GenericSortComparer<T> : ISortComparer<T> | |
{ | |
public GenericSortComparer() | |
{ | |
} | |
public GenericSortComparer(string sortProperty, ListSortDirection sortDirection) | |
: this(TypeDescriptor.GetProperties(typeof(T)).Find(sortProperty, true), sortDirection) | |
{ | |
} | |
public GenericSortComparer(PropertyDescriptor sortProperty, ListSortDirection sortDirection) | |
{ | |
this.SortDirection = sortDirection; | |
this.SortProperty = sortProperty; | |
} | |
public PropertyDescriptor SortProperty { get; set; } | |
public ListSortDirection SortDirection { get; set; } | |
public int Compare(T x, T y) | |
{ | |
if (this.SortProperty == null) | |
return 0; | |
IComparable obj1 = this.SortProperty.GetValue(x) as IComparable; | |
IComparable obj2 = this.SortProperty.GetValue(y) as IComparable; | |
if (obj1 == null || obj2 == null) | |
return 0; | |
if (this.SortDirection == ListSortDirection.Ascending) | |
return (obj1.CompareTo(obj2)); | |
else | |
return (obj2.CompareTo(obj1)); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public interface ISortComparer<T> : IComparer<T> | |
{ | |
PropertyDescriptor SortProperty { get; set; } | |
ListSortDirection SortDirection { get; set; } | |
} |
Interesting. I suppose that could be a matter of what experience you have with different sorting behaviors. I used the Object.CompareTo in order to get the native object's compare logic. I would imagine if the .NET devs wanted to sort like SQL ORDER BY that they would have returned the value similar to your suggestion. It does make one wonder why they chose this direction, but at the same time, I tend to think following their logic baked into the framework would be preferable to a custom override of behavior. Thanks for the discourse!
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Could I suggest an improvement?
Replacing the method Compare in GenericSortComparer the sort behaves like ORDER BY in SQL: the items are sorted putting on top the once with null values if in ascending order or at bottom if in descending order.