Setup
I have a piece of code that is sorting a DataGridView using a custom IComparer:
public class CustomComparer: IComparer
{
public int Compare(object x, object y)
{
DataGridViewRow row1 = (DataGridViewRow)x;
DataGridViewRow row2 = (DataGridViewRow)y;
if (row1.ReadOnly && row2.ReadOnly)
{
return 0;
}
else if (row1.ReadOnly && !row2.ReadOnly)
{
return 1;
}
else
{
return -1;
}
}
Problem
Strangely, when I execute the following line (after populating the rows):
grid.Sort(new CustomComparer());
I get an ArgumentOutOfRangeException with the message “Index was out of range. Parameter: index”.
More Facts
Further investigation revealed the following:
- The DataGridView I’m sorting doesn’t have a BindingSource on it – rows have been manually added.
- The Stack Trace of the error is only one level deep – it occurs on an InternalDictionary in mscorlib
- Strange fact #1 – This only happens if at any point, my custom comparer returns -1 for any of its comparisons
- If I change the Sort method to no longer use my CustomComparer, the exception is not thrown.
Workaround
This last fact led me to rewrite the Compare() method to defer to .NET’s CompareTo method:
DataGridViewRow row1 = (DataGridViewRow)x;
DataGridViewRow row2 = (DataGridViewRow)y;
return row1.ReadOnly.CompareTo(row2.ReadOnly);
Which mysteriously worked. The exception is no longer thrown.
So although I have a workaround, I wonder if anyone has any idea why this could possibly be a fix, and what the issue might have been in the first place. I’ve looked at the implementation of CompareTo and it also returns -1…
juharr is right, but here’s why he’s right:
Your implementation of
Compareis not symmetric, meaning ifrow1.ReadOnly == falseandrow2.ReadOnly == falseyou return-1, meaning “row1is less thanrow2“. If you turn that comparison around with the same values, thenrow2becomes less thanrow1. This is likely confusing the sort algorithm that requiresCompareto be symmetric.The correct comparison should be:
which is likely what
bool.CompareTo(bool)would return, which is why your “workaround” (which is a better solution, in my opinion) works.