Sort a BindingList

J

jehugaleahsa

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
 
M

Marc Gravell

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
 
J

jehugaleahsa

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;
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;
}
}
 
K

karkeras

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

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

Marc Gravell

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
 
M

Marc Gravell

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.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;
}
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top