简体   繁体   中英

DataGridView Custom Sort - How to compare regardless of data type?

I have a DataGridView and I need to use a custom sorter (derived from System.Collections.IComparer). That was working fine, but I noticed that I hadn't quite gotten my comparisons right because it ends up doing a string compare on the cells regardless of their underlying data type. (so, 1, 10, 2) instead of (1, 2, 10).

How can I write a compare function that could appropriately compare columns regardless of their data type?

public int compare(object x, object y)
{
    DataGridViewRow dr1 = (DataGridViewRow)x;
    DataGridViewRow dr2 = (DataGridViewRow)y;

    object cell1 = dr1.Cells["SomeName"].Value;
    object cell2 = dr2.Cells["SomeName"].Value;

    //Compare cell1 and cell 2 based on the data type in
    //dr1.Cells["SomeName"].ValueType.
}

It seems that there a couple of essential parts to any solution to this problem.

  1. Ensure you are comparing like types.
  2. Ensure that you can compare instances of the type.

Here are some ideas to get you started. I've omitted some error checking in the interest of clarity.

Assume:

Type type1 = dr1.Cells["SomeName"].ValueType;
Type type2 = dr2.Cells["SomeName"].ValueType;

Then see if you can coerce one value to the other's type:

if (type1 != type2)
{
    TypeConverter tc1 = TypeDescriptor.GetConverter(type1);
    TypeConverter tc2 = TypeDescriptor.GetConverter(type2);

    if (tc1.CanConvertFrom(type2))
    {
        cell2 = tc1.ConvertFrom(cell2);
        type2 = type1;
    }
    else if (tc1.CanConvertTo(type2))
    { 
        cell1 = tc1.ConvertTo(cell1, type2);
        type1 = type2;
    }
    else if (tc2.CanConvertFrom(type1))
    {
        cell1 = tc2.ConvertFrom(cell1);
        type1 = type2;
    }
    else if (tc2.CanConvertTo(type1))
    { 
        cell2 = tc2.ConvertTo(cell2, type1);
        type2 = type1;
    }
    else // fallback to string comparison
    {
        cell1 = tc1.ConvertToString(cell1);        
        type1 = cell1.GetType();

        cell2 = tc2.ConvertToString(cell2);        
        type2 = cell2.GetType();
    }
    // cell1 and cell2 should be the same type now
}

Now that you have instances of like type, you need to find a way to compare them.

If you are using C# 4, then the dynamic keyword may be your friend:

dynamic c1 = cell1;
try 
{
    int compareResult = c1.CompareTo(cell2);
}
catch(Exception)
{
    // type1 doesn't implement an IComparable-like interface
}

If you aren't using C# 4, you can see if the values implement IComparable :

if (cell1 is IComparable)
{
   int compareResult = ((IComparable)cell1).CompareTo(cell2);
}

Or perhaps it implements a generic IComparable<T> , in which case may need to resort to some reflection trickery:

Type genericComparableType = typeof(IComparable<>);
Type typedComparableType = genericComparableType.MakeGenericType(new Type[] { type1 });
if (typedComparableType.IsInstanceOfType(cell1))
{
    MethodInfo compareTo = typedComparableType.GetMethod("CompareTo", new Type[] { type1 });
    int compareResult = (int)compareTo.Invoke(cell1, new object[] { cell2 });
}

Finally, you can see if Comparer<T>.Default will work, again using some reflection:

Type genericComparerType = typeof(Comparer<>);
Type typedComparerType = genericComparerType.MakeGenericType(new Type[] { type1 });
PropertyInfo defaultProperty = typedComparerType.GetProperty("Default", BindingFlags.Static | BindingFlags.Public);

object defaultComparer = defaultProperty.GetValue(null, null);
MethodInfo compare = defaultComparer.GetType().GetMethod("Compare", new Type[] { type1, type1 });
int compareResult = (int)compare.Invoke(defaultComparer, new object[] { cell1, cell2 });

If none of these work, then you will have to fallback to string comparison.

Cast to the appropriate type

Int32 i1 = Int32.Parse(cell1.Tostring());

Int32 i2 = Int32.Parse(cell2.Tostring());

return i1.Compare(i2);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM