From 25f57600c18b66a5edf2a2ed2c79db4ea49e0410 Mon Sep 17 00:00:00 2001 From: Markus Date: Thu, 14 May 2020 15:54:50 +0200 Subject: [PATCH 1/2] Added failing tests --- .../Data/DefaultValueConverterTests.cs | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/tests/Avalonia.Base.UnitTests/Data/DefaultValueConverterTests.cs b/tests/Avalonia.Base.UnitTests/Data/DefaultValueConverterTests.cs index f70769ac39..efa81dcc1b 100644 --- a/tests/Avalonia.Base.UnitTests/Data/DefaultValueConverterTests.cs +++ b/tests/Avalonia.Base.UnitTests/Data/DefaultValueConverterTests.cs @@ -6,6 +6,7 @@ using System.Windows.Input; using System; using Avalonia.Data.Converters; using Avalonia.Layout; +using System.ComponentModel; namespace Avalonia.Base.UnitTests.Data.Converters { @@ -35,6 +36,54 @@ namespace Avalonia.Base.UnitTests.Data.Converters Assert.Equal(5.0, result); } + [Fact] + public void Do_Not_Throw_On_InvalidInput_For_NullableInt() + { + var result = DefaultValueConverter.Instance.Convert( + "", + typeof(int?), + null, + CultureInfo.InvariantCulture); + + Assert.IsType(typeof(BindingNotification), result); + } + + [Fact] + public void Can_Convert_Decimal_To_NullableDouble() + { + var result = DefaultValueConverter.Instance.Convert( + 5m, + typeof(double?), + null, + CultureInfo.InvariantCulture); + + Assert.Equal(5.0, result); + } + + [Fact] + public void Can_Convert_CustomType_To_Int() + { + var result = DefaultValueConverter.Instance.Convert( + new CustomType(123), + typeof(int), + null, + CultureInfo.InvariantCulture); + + Assert.Equal(123, result); + } + + [Fact] + public void Can_Convert_Int_To_CustomType() + { + var result = DefaultValueConverter.Instance.Convert( + 123, + typeof(CustomType), + null, + CultureInfo.InvariantCulture); + + Assert.Equal(new CustomType(123), result); + } + [Fact] public void Can_Convert_String_To_Enum() { @@ -187,5 +236,44 @@ namespace Avalonia.Base.UnitTests.Data.Converters return v.Value; } } + + [TypeConverter(typeof(CustomTypeConverter))] + private class CustomType { + + public int Value { get; } + + public CustomType(int value) + { + Value = value; + } + + public override bool Equals(object obj) + { + return obj is CustomType other && this.Value == other.Value; + } + } + + private class CustomTypeConverter : TypeConverter + { + public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) + { + return destinationType == typeof(int); + } + + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + { + return sourceType == typeof(int); + } + + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) + { + return ((CustomType)value).Value; + } + + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + { + return new CustomType((int)value); + } + } } } From fd93dc12c4f8dc1d56d5ba88615b0bfda86c6017 Mon Sep 17 00:00:00 2001 From: Markus Date: Thu, 14 May 2020 15:58:03 +0200 Subject: [PATCH 2/2] Fixed handling for Nullable, TypeConverter on from, respect culture --- src/Avalonia.Base/Utilities/TypeUtilities.cs | 43 ++++++++++++-------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/src/Avalonia.Base/Utilities/TypeUtilities.cs b/src/Avalonia.Base/Utilities/TypeUtilities.cs index 66b4676b45..d0d88166a7 100644 --- a/src/Avalonia.Base/Utilities/TypeUtilities.cs +++ b/src/Avalonia.Base/Utilities/TypeUtilities.cs @@ -115,45 +115,46 @@ namespace Avalonia.Utilities return true; } + var toUnderl = Nullable.GetUnderlyingType(to) ?? to; var from = value.GetType(); - if (to.IsAssignableFrom(from)) + if (toUnderl.IsAssignableFrom(from)) { result = value; return true; } - if (to == typeof(string)) + if (toUnderl == typeof(string)) { - result = Convert.ToString(value); + result = Convert.ToString(value, culture); return true; } - if (to.IsEnum && from == typeof(string)) + if (toUnderl.IsEnum && from == typeof(string)) { - if (Enum.IsDefined(to, (string)value)) + if (Enum.IsDefined(toUnderl, (string)value)) { - result = Enum.Parse(to, (string)value); + result = Enum.Parse(toUnderl, (string)value); return true; } } - if (!from.IsEnum && to.IsEnum) + if (!from.IsEnum && toUnderl.IsEnum) { result = null; - if (TryConvert(Enum.GetUnderlyingType(to), value, culture, out object enumValue)) + if (TryConvert(Enum.GetUnderlyingType(toUnderl), value, culture, out object enumValue)) { - result = Enum.ToObject(to, enumValue); + result = Enum.ToObject(toUnderl, enumValue); return true; } } - if (from.IsEnum && IsNumeric(to)) + if (from.IsEnum && IsNumeric(toUnderl)) { try { - result = Convert.ChangeType((int)value, to, culture); + result = Convert.ChangeType((int)value, toUnderl, culture); return true; } catch @@ -164,7 +165,7 @@ namespace Avalonia.Utilities } var convertableFrom = Array.IndexOf(InbuiltTypes, from); - var convertableTo = Array.IndexOf(InbuiltTypes, to); + var convertableTo = Array.IndexOf(InbuiltTypes, toUnderl); if (convertableFrom != -1 && convertableTo != -1) { @@ -172,7 +173,7 @@ namespace Avalonia.Utilities { try { - result = Convert.ChangeType(value, to, culture); + result = Convert.ChangeType(value, toUnderl, culture); return true; } catch @@ -183,15 +184,23 @@ namespace Avalonia.Utilities } } - var typeConverter = TypeDescriptor.GetConverter(to); + var toTypeConverter = TypeDescriptor.GetConverter(toUnderl); + + if (toTypeConverter.CanConvertFrom(from) == true) + { + result = toTypeConverter.ConvertFrom(null, culture, value); + return true; + } + + var fromTypeConverter = TypeDescriptor.GetConverter(from); - if (typeConverter.CanConvertFrom(from) == true) + if (fromTypeConverter.CanConvertTo(toUnderl) == true) { - result = typeConverter.ConvertFrom(null, culture, value); + result = fromTypeConverter.ConvertTo(null, culture, value, toUnderl); return true; } - var cast = FindTypeConversionOperatorMethod(from, to, OperatorType.Implicit | OperatorType.Explicit); + var cast = FindTypeConversionOperatorMethod(from, toUnderl, OperatorType.Implicit | OperatorType.Explicit); if (cast != null) {