diff --git a/src/Perspex.Base/Utilities/TypeUtilities.cs b/src/Perspex.Base/Utilities/TypeUtilities.cs index a8a5ec4d18..3d19de5f97 100644 --- a/src/Perspex.Base/Utilities/TypeUtilities.cs +++ b/src/Perspex.Base/Utilities/TypeUtilities.cs @@ -74,9 +74,8 @@ namespace Perspex.Utilities } else { - var cast = from.GetTypeInfo() - .GetDeclaredMethods("op_Implicit") - .FirstOrDefault(m => m.ReturnType == to); + var cast = from.GetRuntimeMethods() + .FirstOrDefault(m => m.Name == "op_Implicit" && m.ReturnType == to); if (cast != null) { @@ -100,9 +99,27 @@ namespace Perspex.Utilities /// True if the cast was sucessful, otherwise false. public static bool TryConvert(Type to, object value, CultureInfo culture, out object result) { - var valueType = value.GetType(); + if (value == null) + { + result = null; + return AcceptsNull(to); + } + + var from = value.GetType(); + + if (value == PerspexProperty.UnsetValue) + { + result = value; + return true; + } - if (to.GetTypeInfo().IsEnum && valueType == typeof(string)) + if (to.GetTypeInfo().IsAssignableFrom(from.GetTypeInfo())) + { + result = value; + return true; + } + + if (to.GetTypeInfo().IsEnum && from == typeof(string)) { if (Enum.IsDefined(to, (string)value)) { @@ -111,8 +128,12 @@ namespace Perspex.Utilities } } - if ((valueType == typeof(string) && Conversions.ContainsKey(to)) || - (to == typeof(string) && Conversions.ContainsKey(value.GetType()))) + bool containsFrom = Conversions.ContainsKey(from); + bool containsTo = Conversions.ContainsKey(to); + + if ((containsFrom && containsTo) || + (from == typeof(string) && containsTo) || + (to == typeof(string) && containsFrom)) { try { @@ -125,10 +146,18 @@ namespace Perspex.Utilities return false; } } - else + + var cast = from.GetRuntimeMethods() + .FirstOrDefault(m => (m.Name == "op_Implicit" || m.Name == "op_Explicit") && m.ReturnType == to); + + if (cast != null) { - return TryCast(to, value, out result); + result = cast.Invoke(null, new[] { value }); + return true; } + + result = null; + return false; } /// diff --git a/tests/Perspex.Markup.UnitTests/DefaultValueConverterTests.cs b/tests/Perspex.Markup.UnitTests/DefaultValueConverterTests.cs index 59b0d1c422..b438d6af1d 100644 --- a/tests/Perspex.Markup.UnitTests/DefaultValueConverterTests.cs +++ b/tests/Perspex.Markup.UnitTests/DefaultValueConverterTests.cs @@ -32,6 +32,18 @@ namespace Perspex.Markup.UnitTests Assert.Equal(5.0, result); } + [Fact] + public void Can_Convert_String_To_Enum() + { + var result = DefaultValueConverter.Instance.Convert( + "Bar", + typeof(TestEnum), + null, + CultureInfo.InvariantCulture); + + Assert.Equal(TestEnum.Bar, result); + } + [Fact] public void Can_Convert_Double_To_String() { @@ -43,5 +55,50 @@ namespace Perspex.Markup.UnitTests Assert.Equal("5", result); } + + [Fact] + public void Can_Convert_Double_To_Int() + { + var result = DefaultValueConverter.Instance.Convert( + 5.0, + typeof(int), + null, + CultureInfo.InvariantCulture); + + Assert.Equal(5, result); + } + + [Fact] + public void Can_Use_Explicit_Cast() + { + var result = DefaultValueConverter.Instance.Convert( + new ExplicitDouble(5.0), + typeof(double), + null, + CultureInfo.InvariantCulture); + + Assert.Equal(5.0, result); + } + + private enum TestEnum + { + Foo, + Bar, + } + + private class ExplicitDouble + { + public ExplicitDouble(double value) + { + Value = value; + } + + public double Value { get; } + + public static explicit operator double (ExplicitDouble v) + { + return v.Value; + } + } } }