[binding] return defaultValueCreator on failure (#4541)
authorStephane Delcroix <stephane@delcroix.org>
Thu, 29 Nov 2018 08:29:49 +0000 (09:29 +0100)
committerGitHub <noreply@github.com>
Thu, 29 Nov 2018 08:29:49 +0000 (09:29 +0100)
if the FallbackValue isn't set, returns the value created by the
defaultValueCreator instead of the DefaultValue when a binding can not
be resolved.

- fixes #2752

Xamarin.Forms.Core/BindingExpression.cs
Xamarin.Forms.Core/TypedBinding.cs
Xamarin.Forms.Xaml.UnitTests/Issues/Gh2752.xaml [new file with mode: 0644]
Xamarin.Forms.Xaml.UnitTests/Issues/Gh2752.xaml.cs [new file with mode: 0644]

index c733d5b..9f6dd05 100644 (file)
@@ -152,12 +152,11 @@ namespace Xamarin.Forms
                        if (needsGetter)
                        {
                                object value = property.DefaultValue;
-                               if (part.TryGetValue(current, out value) || part.IsSelf)
-                               {
+                               if (part.TryGetValue(current, out value) || part.IsSelf) {
                                        value = Binding.GetSourceValue(value, property.ReturnType);
                                }
                                else
-                                       value = Binding.FallbackValue ?? property.DefaultValue;
+                                       value = Binding.FallbackValue ?? property.GetDefaultValue(target);
 
                                if (!TryConvert(ref value, property, property.ReturnType, true))
                                {
index c8afff7..db71fd9 100644 (file)
@@ -193,7 +193,7 @@ namespace Xamarin.Forms.Internals
                                Subscribe((TSource)sourceObject);
 
                        if (needsGetter) {
-                               var value = FallbackValue ?? property.DefaultValue;
+                               var value = FallbackValue ?? property.GetDefaultValue(target);
                                if (isTSource) {
                                        try {
                                                value = GetSourceValue(_getter((TSource)sourceObject), property.ReturnType);
diff --git a/Xamarin.Forms.Xaml.UnitTests/Issues/Gh2752.xaml b/Xamarin.Forms.Xaml.UnitTests/Issues/Gh2752.xaml
new file mode 100644 (file)
index 0000000..dc4fa1a
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ContentPage
+               xmlns="http://xamarin.com/schemas/2014/forms"
+               xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
+               xmlns:local="using:Xamarin.Forms.Xaml.UnitTests"
+               x:Class="Xamarin.Forms.Xaml.UnitTests.Gh2752"
+               x:DataType="local:Gh2752VM"
+               My="{Binding Foo.Bar.Baz}">
+</ContentPage>
\ No newline at end of file
diff --git a/Xamarin.Forms.Xaml.UnitTests/Issues/Gh2752.xaml.cs b/Xamarin.Forms.Xaml.UnitTests/Issues/Gh2752.xaml.cs
new file mode 100644 (file)
index 0000000..ffa7efa
--- /dev/null
@@ -0,0 +1,44 @@
+using System;
+using System.Collections.Generic;
+using NUnit.Framework;
+using Xamarin.Forms;
+using Xamarin.Forms.Core.UnitTests;
+
+namespace Xamarin.Forms.Xaml.UnitTests
+{
+       public class Gh2752VM {
+               public Gh2752VM Foo { get; set; }
+               public Gh2752VM Bar { get; set; }
+               public string Baz { get; set; }
+       }
+       public partial class Gh2752 : ContentPage
+       {
+               public static readonly BindableProperty MyProperty =
+                       BindableProperty.Create("My", typeof(string), typeof(Gh2752), default(string), defaultValueCreator: b=> "default created value");
+
+               public string My {
+                       get { return (string)GetValue(MyProperty); }
+                       set { SetValue(MyProperty, value); }
+               }
+
+               public Gh2752() => InitializeComponent();
+               public Gh2752(bool useCompiledXaml)
+               {
+                       //this stub will be replaced at compile time
+               }
+
+               [TestFixture]
+               class Tests
+               {
+                       [SetUp] public void Setup() => Device.PlatformServices = new MockPlatformServices();
+                       [TearDown] public void TearDown() => Device.PlatformServices = null;
+
+                       [TestCase(true), TestCase(false)]
+                       public void FallbcakToDefaultValueCreator(bool useCompiledXaml)
+                       {
+                               var layout = new Gh2752(useCompiledXaml) { BindingContext = null };
+                               Assert.That(layout.My, Is.EqualTo("default created value"));
+                       }
+               }
+       }
+}
\ No newline at end of file