Sorting Bindinglist based DataGridView

A

Andrus

I have 3.5 WinForms DataGridView whose DataSource is BindingList<TEntity>.
I need to sort when user clicks in column header.

I tried MSDN sample but

Sort(newColumn, direction);

causes exception:

DataGridView control cannot be sorted if it is bound to an IBindingList that
does not support sorting.

How to allow user to sort in this DataGridView ?

Andrus.
 
A

Andrus

RobinS,
Assuming that TEntity is a class, here's an example of what you need to
do. This code goes in the class that is the BindingList<TEntity>.

Thank you. I have custom Combobox cell in DataGridView

class ComboBoxCell : DataGridViewComboBoxCell {

protected override object GetFormattedValue(object value,
int rowIndex, ref DataGridViewCellStyle cellStyle,
TypeConverter valueTypeConverter,
TypeConverter formattedValueTypeConverter,
DataGridViewDataErrorContexts context) {
.....
return base.GetFormattedValue(value, rowIndex, ref cellStyle,
valueTypeConverter, formattedValueTypeConverter, context);
}

Using your class causes exception when form is opened in
base.GetFormattedValue()

System.NotSupportedException was unhandled
Message="Specified method is not supported."
Source="System"
StackTrace:
at System.ComponentModel.BindingList`1.FindCore(PropertyDescriptor
prop, Object key)
at
System.ComponentModel.BindingList`1.System.ComponentModel.IBindingList.Find(PropertyDescriptor
prop, Object key)
at
System.Windows.Forms.DataGridViewComboBoxCell.ItemFromComboBoxDataSource(PropertyDescriptor
property, Object key)
at
System.Windows.Forms.DataGridViewComboBoxCell.LookupDisplayValue(Int32
rowIndex, Object value, Object& displayValue)
at
System.Windows.Forms.DataGridViewComboBoxCell.GetFormattedValue(Object
value, Int32 rowIndex, DataGridViewCellStyle& cellStyle, TypeConverter
valueTypeConverter, TypeConverter formattedValueTypeConverter,
DataGridViewDataErrorContexts context)
.....

If I use normal BindingList class, grid displays without error.

How to fix ?

Linq as OrderBy method.
I'snt it more resonable to use its OrderBy() method instead of re-inventing
wheel by creating own comparer ?
This would reduce the amount of code a lot.
If yes, how to implement this with les code ?

Andrus.
 
M

Marc Gravell

If I use normal BindingList class, grid displays without error.
How to fix ?
I don't know why it behaves differently to a regular BindingList<T> -
but note that it isn't that hard to implement IBindingList.Find - it
might even be as simple as below (from a BindingList said:
I'snt it more resonable to use its OrderBy() method instead of re-inventing
wheel by creating own comparer ?
No; the standard Enumerable.OrderBy returns a *different* list, where-
as we want to sort the one in-place. I have played with some IList/
IList<T> extension methods for in-place, and I believe that Jon has
also taken a look, but standard Enumerable isn't an option here. I
have posted several sortable BindingList<T> implementations over the
years - not sure if this one is any beter / worse (I haven't looked in
great detail) - but there isn't that much available to you.
List<T>.Sort with a comparer is a reasonable option.

Marc

Find suggeston (not tested):


protected override bool SupportsSearchingCore
{
get
{
return true;
}
}
protected override int FindCore(PropertyDescriptor prop,
object key)
{
if(prop == null) throw new ArgumentNullException("prop");
for (int i = 0; i < Count; i++)
{
object item = this;
if (item != null && object.Equals(key,
prop.GetValue(item))) {
return i;
}
}
return -1;
}
 
A

Andrus

Marc,
I don't know why it behaves differently to a regular BindingList<T> -
but note that it isn't that hard to implement IBindingList.Find - it
might even be as simple as below (from a BindingList<T> subclass)

I'm sorry I don't understand this code.

If I create SortableBindingList<T>: BindingList<T> { },
add those two methods to this class,
instantiate it using List<T> as constructor parameter
and pass result list to DataGridView DataSource

should sorting work OK in this case ?
No; the standard Enumerable.OrderBy returns a *different* list, where-
as we want to sort the one in-place. I have played with some IList/
IList<T> extension methods for in-place, and I believe that Jon has
also taken a look, but standard Enumerable isn't an option here. I
have posted several sortable BindingList<T> implementations over the
years - not sure if this one is any beter / worse (I haven't looked in
great detail) - but there isn't that much available to you.
List<T>.Sort with a comparer is a reasonable option.

Isn't it more reasonable to replace DataGridView DataSource with new sorted
BindingList to perform sorting?
In this case it is possible to use dynamic OrderBy method probably.

Andrus.
 
M

Marc Gravell

Isn't it more reasonable to replace DataGridView DataSource with new sorted
BindingList to perform sorting?

Not if you want it integrated with DGV and other binding controls.

But yes: another viable option would be to track the sort manually and
change the entire binding each time.

Marc
 
A

Andrus

RobinS,
For your GetFormattedValue, I would implement the Find method as described
by the other user.

Thank you. After adding Marc Find() method excaption does not more occur.
Can you confirm that the code you posted was incomplete: it did'nt contain
Find() method.

Andrus.
 
A

Andrus

RobinS,

This code does not work with null values.
In int CompareValues(object xValue, object yValue, ListSortDirection
direction)

at line

else if (!xValue.Equals(yValue)) {

I got NullReferenceExcpetion since xValue is null.

Should I re-invent wheel by fixing this myself or is there solution which
work with null values also ?

Andrus.
 
M

Marc Gravell

is there solution which work with null values also ?

int retValue= Comparer.Compare(xValue, yValue);
return direction == ListSortDirection.Ascending ? retValue : -
retValue;

Marc
 
A

Andrus

is there solution which work with null values also ?
int retValue= Comparer.Compare(xValue, yValue);
return direction == ListSortDirection.Ascending ? retValue : -
retValue;

Marc,

thank you.

int retValue= Comparer.Compare(xValue, yValue);

causes compile time error

Using the generic type 'System.Collections.Generic.Comparer<T>' requires '1'
type arguments


Using refactor offering I changed this line to

int retValue = System.Collections.Comparer.Compare(xValue, yValue);

but now got error

An object reference is required for the non-static field, method, or
property 'System.Collections.Comparer.Compare(object, object)'


So I finally changed method to:

int IComparer<T>.Compare(T x, T y) {
object xValue = m_PropDesc.GetValue(x);
object yValue = m_PropDesc.GetValue(y);
if (xValue == null && yValue == null)
return 0;
if (xValue == null && yValue != null)
return 1;
if (xValue != null && yValue == null)
return -1;
return CompareValues(xValue, yValue, m_Direction);
}

is this best style and solution ?

Andrus.
 
A

Andrus

RobinS,
It did not include the Find method because I didn't need a find method.
You asked me how to SORT, not how to FIND. :)

I don't use Find() in my code.

DataGridView requires Find() method implementation to show formatted value,
no idea why.

Andrus.
 

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