[C] Invoke ToString while setting string BP (#7651)
authorStephane Delcroix <stephane@delcroix.org>
Fri, 27 Sep 2019 00:52:38 +0000 (02:52 +0200)
committerSamantha Houts <samhouts@users.noreply.github.com>
Fri, 27 Sep 2019 00:52:38 +0000 (17:52 -0700)
* [C] Invoke ToString while setting string BP

Invoke ToString with the CurrentUICulture when seeting a property of type string
from a non-convertible object.

- fixes #6281

* Update Xamarin.Forms.Core/BindableProperty.cs

Xamarin.Forms.Core.UnitTests/BindableObjectUnitTests.cs
Xamarin.Forms.Core/BindableProperty.cs
Xamarin.Forms.Core/ToStringValueConverter.cs

index 0760212..f2082db 100644 (file)
@@ -1562,5 +1562,34 @@ namespace Xamarin.Forms.Core.UnitTests
 
                        Assert.That(() => binding.Path = "Foo", Throws.Nothing);
                }
+
+
+               class InfoToString
+               {
+                       public override string ToString() => "converted";
+               }
+
+               [Test]
+               //https://github.com/xamarin/Xamarin.Forms/issues/6281
+               public void SetValueToTextInvokesToString()
+               {
+                       var prop = BindableProperty.Create("foo", typeof(string), typeof(MockBindable), null);
+                       var bindable = new MockBindable();
+                       bindable.SetValue(prop, new InfoToString());
+
+                       Assert.That(bindable.GetValue(prop), Is.EqualTo("converted"));
+               }
+
+               [Test]
+               //https://github.com/xamarin/Xamarin.Forms/issues/6281
+               public void SetBindingToTextInvokesToString()
+               {
+                       var prop = BindableProperty.Create("foo", typeof(string), typeof(MockBindable), null);
+                       var bindable = new MockBindable() { BindingContext = new { info = new InfoToString() } };
+                       bindable.SetBinding(prop, "info");
+
+                       Assert.That(bindable.GetValue(prop), Is.EqualTo("converted"));
+               }
+
        }
 }
index e1f759d..05996e5 100644 (file)
@@ -2,6 +2,7 @@
 using System.Collections.Generic;
 using System.ComponentModel;
 using System.Diagnostics;
+using System.Globalization;
 using System.Linq.Expressions;
 using System.Reflection;
 using Xamarin.Forms.Internals;
@@ -33,12 +34,17 @@ namespace Xamarin.Forms
 
                public delegate bool ValidateValueDelegate<in TPropertyType>(BindableObject bindable, TPropertyType value);
 
-               static readonly Dictionary<Type, TypeConverter> WellKnownConvertTypes = new  Dictionary<Type,TypeConverter>
+               static readonly Dictionary<Type, TypeConverter> KnownTypeConverters = new Dictionary<Type,TypeConverter>
                {
                        { typeof(Uri), new UriTypeConverter() },
                        { typeof(Color), new ColorTypeConverter() },
                };
 
+               static readonly Dictionary<Type, IValueConverter> KnownIValueConverters = new Dictionary<Type, IValueConverter>
+               {
+                       { typeof(string), new ToStringValueConverter() },
+               };
+
                // more or less the encoding of this, without the need to reflect
                // http://msdn.microsoft.com/en-us/library/y5b434w4.aspx
                static readonly Dictionary<Type, Type[]> SimpleConvertTypes = new Dictionary<Type, Type[]>
@@ -52,7 +58,7 @@ namespace Xamarin.Forms
                        { typeof(long), new[] { typeof(string), typeof(float), typeof(double), typeof(decimal) } },
                        { typeof(char), new[] { typeof(string), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(float), typeof(double), typeof(decimal) } },
                        { typeof(float), new[] { typeof(string), typeof(double) } },
-                       { typeof(ulong), new[] { typeof(string), typeof(float), typeof(double), typeof(decimal) } }
+                       { typeof(ulong), new[] { typeof(string), typeof(float), typeof(double), typeof(decimal) } },
                };
 
                BindableProperty(string propertyName, Type returnType, Type declaringType, object defaultValue, BindingMode defaultBindingMode = BindingMode.OneWay,
@@ -312,38 +318,36 @@ namespace Xamarin.Forms
                internal bool TryConvert(ref object value)
                {
                        if (value == null)
-                       {
                                return !ReturnTypeInfo.IsValueType || ReturnTypeInfo.IsGenericType && ReturnTypeInfo.GetGenericTypeDefinition() == typeof(Nullable<>);
-                       }
 
                        Type valueType = value.GetType();
                        Type type = ReturnType;
 
                        // Dont support arbitrary IConvertible by limiting which types can use this
-                       Type[] convertableTo;
-                       TypeConverter typeConverterTo;
-                       if (SimpleConvertTypes.TryGetValue(valueType, out convertableTo) && Array.IndexOf(convertableTo, type) != -1)
-                       {
+                       if (SimpleConvertTypes.TryGetValue(valueType, out Type[]  convertibleTo) && Array.IndexOf( convertibleTo, type) != -1) {
                                value = Convert.ChangeType(value, type);
+                               return true;
                        }
-                       else if (WellKnownConvertTypes.TryGetValue(type, out typeConverterTo) && typeConverterTo.CanConvertFrom(valueType))
-                       {
+                       if (KnownTypeConverters.TryGetValue(type, out TypeConverter typeConverterTo) && typeConverterTo.CanConvertFrom(valueType)) {
                                value = typeConverterTo.ConvertFromInvariantString(value.ToString());
+                               return true;
                        }
-                       else if (!ReturnTypeInfo.IsAssignableFrom(valueType.GetTypeInfo()))
-                       {
-                               var cast = type.GetImplicitConversionOperator(fromType: valueType, toType: type)
-                                               ?? valueType.GetImplicitConversionOperator(fromType: valueType, toType: type);
-
-                               if (cast == null)
-                                       return false;
+                       if (ReturnTypeInfo.IsAssignableFrom(valueType.GetTypeInfo()))
+                               return true;
 
+                       var cast = type.GetImplicitConversionOperator(fromType: valueType, toType: type) ?? valueType.GetImplicitConversionOperator(fromType: valueType, toType: type);
+                       if (cast != null) {
                                value = cast.Invoke(null, new[] { value });
+                               return true;
+                       }
+                       if (KnownIValueConverters.TryGetValue(type, out IValueConverter valueConverter)) {
+                               value = valueConverter.Convert(value, type, null, CultureInfo.CurrentUICulture);
+                               return true;
                        }
 
-                       return true;
+                       return false;
                }
 
                internal delegate void BindablePropertyBindingChanging(BindableObject bindable, BindingBase oldValue, BindingBase newValue);
        }
-}
\ No newline at end of file
+}
index 0b36f8d..3da2208 100644 (file)
@@ -8,21 +8,14 @@ namespace Xamarin.Forms
                public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
                {
                        if (value == null)
-                       {
                                return null;
-                       }
 
                        if (value is IFormattable formattable)
-                       {
                                return formattable.ToString(parameter?.ToString(), culture);
-                       }
 
                        return value.ToString();
                }
 
-               public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
-               {
-                       throw new NotSupportedException();
-               }
+               public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new NotSupportedException();
        }
 }