Question

I have a DataGridView component populated with a SortableBindingList, as found in the SQL Server SDK, Microsoft.SqlServer.Management.Controls. This class is also used as the root node for XML serialization.

The members of the list are of a type with two primitive fields, and one object field. The columns which contain primitive values sort as one would expect them to. However, when sorting the column containing the object fields, the following exception is thrown:

System.InvalidOperationException was unhandled
  Message=Failed to compare two elements in the array.
  Source=mscorlib
  StackTrace:
       at System.Collections.Generic.ArraySortHelper`1.Sort(T[] keys, Int32 index, Int32 length, IComparer`1 comparer)
       at System.Array.Sort[T](T[] array, Int32 index, Int32 length, IComparer`1 comparer)
       at System.Collections.Generic.List`1.Sort(Comparison`1 comparison)
       at Microsoft.SqlServer.Management.Controls.SortableBindingList`1.ApplySortCore(PropertyDescriptor prop, ListSortDirection direction)
       at System.ComponentModel.BindingList`1.System.ComponentModel.IBindingList.ApplySort(PropertyDescriptor prop, ListSortDirection direction)
       at System.Windows.Forms.BindingSource.ApplySort(PropertyDescriptor property, ListSortDirection sort)
       ...
  InnerException: System.ArgumentException
       Message=At least one object must implement IComparable.
       Source=mscorlib
       StackTrace:
            at System.Collections.Comparer.Compare(Object a, Object b)
            at Microsoft.SqlServer.Management.Controls.SortableBindingList`1.<>c__DisplayClass1.<GetComparisionDelegate>b__0(T t1, T t2)
            at System.Array.FunctorComparer`1.Compare(T x, T y)
            at System.Collections.Generic.ArraySortHelper`1.SwapIfGreaterWithItems(T[] keys, IComparer`1 comparer, Int32 a, Int32 b)
            at System.Collections.Generic.ArraySortHelper`1.QuickSort(T[] keys, Int32 left, Int32 right, IComparer`1 comparer)
            at System.Collections.Generic.ArraySortHelper`1.Sort(T[] keys, Int32 index, Int32 length, IComparer`1 comparer)

It seems fairly clear from the stack trace that the objects which are being compared are not IComparable - except that they are. Or at least they should be. The class in question is the following:

using System;
using System.Xml.Serialization;

namespace myapp.xmlobjects {
    [XmlType("error_after")]
    public class ErrorAfterObject : IComparable<ErrorAfterObject> {

        [XmlAttribute("hours")]
        public int Hours { get; set; }

        [XmlAttribute("minutes")]
        public int Minutes { get; set; }

        public ErrorAfterObject() { }

        public ErrorAfterObject(int hours, int minutes) {
            this.Hours = hours;
            this.Minutes = minutes;
        }

        public override string ToString() {
            return string.Format("{0} hr {1} min", this.Hours, this.Minutes);
        }

        public int CompareTo(ErrorAfterObject other) {
            return (this.Hours*60 + this.Minutes).CompareTo(other.Hours*60 + other.Minutes);
        }
    }
}

As a sanity check, I added the following debug code after data binding:

Console.WriteLine(myGridView.Rows[0].Cells[2].ValueType.ToString());

which comes back as myapp.xmlobjects.ErrorAfterObject as I would expect.

Three questions which may help to resolve the problem: Are my objects being typed as something uncomparable? Is it possible to check exactly what object types are being compared? Have I missed something in the IComparable implementation?

Thanks in advance.

Was it helpful?

Solution

As it turns out, IComparable and IComparable<T> are not the same thing. Replacing the class definition with:

public class ErrorAfterObject : IComparable

and the CompareTo method with:

public int CompareTo(object other) {
    if(this.GetType() != other.GetType()) {
        return Comparer.Default.Compare(this, other);
    }
    return (this.Hours*60 + this.Minutes).CompareTo(
        ((ErrorAfterObject)other).Hours*60 + ((ErrorAfterObject)other).Minutes);
}

works as one expects.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top