Sort a BindingList

545 views
Skip to first unread message

jehuga...@gmail.com

unread,
Jan 2, 2008, 9:39:07 AM1/2/08
to
Hello:

I want to know the best way to sort a BindingList without needed to
turn it into a DataTable. Of course, my BindingList is being used
indirectly by a BindingSource.

So, how do I sort a BindingList of my custom business objects? For
instance, when the user clicks on a column header of a DataGridView, I
would like sorting to be handled for me (or to call a custom sort
method I provide).

If this isn't supported, that would be good to know.

Thanks,
Travis

Marc Gravell

unread,
Jan 2, 2008, 11:11:51 AM1/2/08
to
If I remember, it isn't supported out-of-the-box, but you can inherit
from BindingList and provide an implementation for sort (and the
matching "is supported" property to return true). The trick is to
provide suitable implementations for IBindingList (single sort) and/or
IBindingListView (multi-sort).

I have a full implementation, but can't access it until tomorrow. Let
me know if you want me to post some of this (although it isn't too
hard).

Marc

jehuga...@gmail.com

unread,
Jan 2, 2008, 12:31:11 PM1/2/08
to
You're right. It is fairly simple. Thanks for the information.

public class SortableBindingList<T> : BindingList<T>
{
protected override bool SupportsSortingCore
{
get
{
return true;
}
}

protected override bool IsSortedCore
{
get
{
for (int i = 0; i < Items.Count - 1; ++i)
{
T lhs = Items[i];
T rhs = Items[i + 1];
PropertyDescriptor property = SortPropertyCore;
if (property != null)
{
object lhsValue = lhs == null ? null :
property.GetValue(lhs);
object rhsValue = rhs == null ? null :
property.GetValue(rhs);
int result;
if (lhsValue == null)
{
result = -1;
}
else if (rhsValue == null)
{
result = 1;
}
else
{
result =
Comparer.Default.Compare(lhsValue, rhsValue);
}
if (SortDirectionCore ==
ListSortDirection.Descending)
{
result = -result;
}
if (result >= 0)
{
return false;
}
}
}
return true;
}
}

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

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

protected override void ApplySortCore(PropertyDescriptor prop,
ListSortDirection direction)
{
sortProperty = prop;
sortDirection = direction;

List<T> list = (List<T>)Items;
list.Sort(delegate(T lhs, T rhs)
{
if (sortProperty != null)
{
object lhsValue = lhs == null ? null :
sortProperty.GetValue(lhs);
object rhsValue = rhs == null ? null :
sortProperty.GetValue(rhs);
int result;
if (lhsValue == null)
{
result = -1;
}
else if (rhsValue == null)
{
result = 1;
}
else
{
result = Comparer.Default.Compare(lhsValue,
rhsValue);
}
if (sortDirection == ListSortDirection.Descending)
{
result = -result;
}
return result;
}
else
{
return 0;
}
});
}

protected override void RemoveSortCore()
{
sortDirection = ListSortDirection.Ascending;
sortProperty = null;
}
}

Marc Gravell

unread,
Jan 2, 2008, 5:42:29 PM1/2/08
to
Cool; I'm glad you got it working. The documentation isn't 100% clear,
but actually I suspect that IsSortedCore could have simply checked
whether sortProperty != null, but it probably works this way, too.

http://msdn2.microsoft.com/en-us/library/system.componentmodel.ibindinglist.issorted(VS.80).aspx

Marc

jehuga...@gmail.com

unread,
Jan 3, 2008, 9:32:15 AM1/3/08
to
On Jan 2, 3:42 pm, Marc Gravell <marc.grav...@gmail.com> wrote:
> Cool; I'm glad you got it working. The documentation isn't 100% clear,
> but actually I suspect that IsSortedCore could have simply checked
> whether sortProperty != null, but it probably works this way, too.
>
> http://msdn2.microsoft.com/en-us/library/system.componentmodel.ibindi...
>
> Marc

That actually leaves a much more efficient implementation, since I am
guarunteed it is sorted given.

Thanks,
Travis

kark...@gmail.com

unread,
Jan 3, 2008, 6:31:44 PM1/3/08
to

could you please post the multi sort example...thanks in advance!!

Marc Gravell

unread,
Jan 3, 2008, 7:12:06 PM1/3/08
to
I will dig some code out - but basically it involves writing a
composite (chaining) IComparer<T> - i.e. for each property in turn it
does the comparison (as you have already), until a non-zero is found
(or the last comparer (= last property) is found).

Annoyingly, I have a *near* identical block of code "on hand" for some
3.5 LINQ stuff, but alas the gap between "near" and "is" (plus the 3.5
link) is too far to be tweaked quickly...

I'm just trying to seee what I can find... shouldn't bee too long...

Marc

Marc Gravell

unread,
Jan 3, 2008, 7:52:14 PM1/3/08
to
Something like (untested):


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Collections;

public class AdvancedList<T> : BindingList<T>, IBindingListView {
protected override bool IsSortedCore {
get { return sorts != null; }
}
protected override void RemoveSortCore() {
sorts = null;


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

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


}
protected override void ApplySortCore(PropertyDescriptor prop,
ListSortDirection direction) {

ListSortDescription[] arr = {
new ListSortDescription(prop, direction)
};
ApplySort(new ListSortDescriptionCollection(arr));
}
PropertyComparerCollection<T> sorts;
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);
}
sorts = tmp;
} finally {
RaiseListChangedEvents = oldRaise;
ResetBindings();
}
}
string IBindingListView.Filter {
get { throw new NotImplementedException(); }
set { throw new NotImplementedException(); }
}
void IBindingListView.RemoveFilter() {
throw new NotImplementedException();
}
ListSortDescriptionCollection IBindingListView.SortDescriptions {
get { return sorts.Sorts; }
}
bool IBindingListView.SupportsAdvancedSorting {
get { return true; }
}
bool IBindingListView.SupportsFiltering {
get { return false; }
}
}
public class PropertyComparerCollection<T> : IComparer<T> {
private readonly ListSortDescriptionCollection sorts;
private readonly PropertyComparer<T>[] comparers;
public ListSortDescriptionCollection Sorts {
get { return 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));
}
comparers = list.ToArray();
}
public PropertyDescriptor PrimaryProperty {
get {
return comparers.Length == 0 ? null :
comparers[0].Property;
}
}
public ListSortDirection PrimaryDirection {
get {
return comparers.Length == 0 ? ListSortDirection.Ascending
: comparers[0].Descending ?
ListSortDirection.Descending
: ListSortDirection.Ascending;
}
}

int IComparer<T>.Compare(T x, T y) {
int result = 0;
for (int i = 0; i < comparers.Length; i++) {
result = comparers[i].Compare(x, y);
if (result != 0) break;
}
return result;
}
}

public class PropertyComparer<T> : IComparer<T> {
private readonly bool descending;
public bool Descending { get { return descending; } }
private readonly PropertyDescriptor property;
public PropertyDescriptor Property { get { return property; } }
public PropertyComparer(PropertyDescriptor property, bool
descending) {
if (property == null) throw new
ArgumentNullException("property");
this.descending = descending;
this.property = property;
}
public int Compare(T x, T y) {
// todo; some null cases
int value = Comparer.Default.Compare(property.GetValue(x),
property.GetValue(y));
return descending ? -value : value;
}
}

wpfmentor

unread,
Dec 11, 2008, 6:09:51 AM12/11/08
to
If you are using WPF and want to sort your BindingList using a CollectionViewSource there's a great article here:

http://wpfmentor.blogspot.com/2008/12/observable-collections-independent-of.html

Reply all
Reply to author
Forward
0 new messages