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