[Xaml[C]] cast to BindingBase before SetBinding() (#709)
authorStephane Delcroix <stephane@delcroix.org>
Wed, 1 Feb 2017 16:03:53 +0000 (17:03 +0100)
committerGitHub <noreply@github.com>
Wed, 1 Feb 2017 16:03:53 +0000 (17:03 +0100)
Xamarin.Forms.Build.Tasks/SetPropertiesVisitor.cs
Xamarin.Forms.Xaml.UnitTests/Issues/Bz44213.xaml [new file with mode: 0644]
Xamarin.Forms.Xaml.UnitTests/Issues/Bz44213.xaml.cs [new file with mode: 0644]
Xamarin.Forms.Xaml.UnitTests/Xamarin.Forms.Xaml.UnitTests.csproj
Xamarin.Forms.Xaml/ApplyPropertiesVisitor.cs

index 4a8fd70..8d087ab 100644 (file)
@@ -724,7 +724,7 @@ namespace Xamarin.Forms.Build.Tasks
                        if (CanAdd(parent, localName, valueNode, context))
                                return Add(parent, localName, valueNode, iXmlLineInfo, context);
 
-                       throw new XamlParseException($"No property, bindable property, or event found for '{localName}'", iXmlLineInfo);
+                       throw new XamlParseException($"No property, bindable property, or event found for '{localName}', or mismatching type between value and property.", iXmlLineInfo);
                }
 
                static FieldReference GetBindablePropertyReference(VariableDefinition parent, string namespaceURI, ref string localName, out bool attached, ILContext context, IXmlLineInfo iXmlLineInfo)
@@ -838,6 +838,10 @@ namespace Xamarin.Forms.Build.Tasks
                        VariableDefinition varValue;
                        if (!context.Variables.TryGetValue(valueNode as IElementNode, out varValue))
                                return false;
+                       var implicitOperator = varValue.VariableType.GetImplicitOperatorTo(module.Import(typeof(BindingBase)), module);
+                       if (implicitOperator != null)
+                               return true;
+
                        return varValue.VariableType.InheritsFromOrImplements(module.Import(typeof(BindingBase)));
                }
 
@@ -845,6 +849,7 @@ namespace Xamarin.Forms.Build.Tasks
                {
                        var module = context.Body.Method.Module;
                        var varValue = context.Variables [elementNode];
+                       var implicitOperator = varValue.VariableType.GetImplicitOperatorTo(module.Import(typeof(BindingBase)), module);
 
                        //TODO: check if parent is a BP
                        var setBinding = typeof(BindableObject).GetMethod("SetBinding", new [] { typeof(BindableProperty), typeof(BindingBase) });
@@ -852,6 +857,9 @@ namespace Xamarin.Forms.Build.Tasks
                        yield return Instruction.Create(OpCodes.Ldloc, parent);
                        yield return Instruction.Create(OpCodes.Ldsfld, bpRef);
                        yield return Instruction.Create(OpCodes.Ldloc, varValue);
+                       if (implicitOperator != null) 
+//                             IL_000f:  call !0 class [Xamarin.Forms.Core]Xamarin.Forms.OnPlatform`1<BindingBase>::op_Implicit(class [Xamarin.Forms.Core]Xamarin.Forms.OnPlatform`1<!0>)
+                               yield return Instruction.Create(OpCodes.Call, module.Import(implicitOperator));
                        yield return Instruction.Create(OpCodes.Callvirt, module.Import(setBinding));
                }
 
diff --git a/Xamarin.Forms.Xaml.UnitTests/Issues/Bz44213.xaml b/Xamarin.Forms.Xaml.UnitTests/Issues/Bz44213.xaml
new file mode 100644 (file)
index 0000000..0a932f3
--- /dev/null
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Xamarin.Forms.Xaml.UnitTests.Bz44213">
+       <Label x:Name="label">
+               <Label.Text>
+                       <OnPlatform x:TypeArguments="BindingBase" Android="{Binding Bar}">
+                               <OnPlatform.iOS>
+                                       <Binding Path="Foo"/>
+                               </OnPlatform.iOS>
+                       </OnPlatform>
+               </Label.Text>
+       </Label>
+</ContentPage>
diff --git a/Xamarin.Forms.Xaml.UnitTests/Issues/Bz44213.xaml.cs b/Xamarin.Forms.Xaml.UnitTests/Issues/Bz44213.xaml.cs
new file mode 100644 (file)
index 0000000..faabad6
--- /dev/null
@@ -0,0 +1,48 @@
+using NUnit.Framework;
+using Xamarin.Forms.Core.UnitTests;
+
+namespace Xamarin.Forms.Xaml.UnitTests
+{
+       public partial class Bz44213 : ContentPage
+       {
+               public Bz44213()
+               {
+                       InitializeComponent();
+               }
+
+               public Bz44213(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 BindingInOnPlatform(bool useCompiledXaml)
+                       {
+                               ((MockPlatformServices)Device.PlatformServices).RuntimePlatform = Device.iOS;
+                               var p = new Bz44213(useCompiledXaml);
+                               p.BindingContext = new { Foo = "Foo", Bar = "Bar" };
+                               Assert.AreEqual("Foo", p.label.Text);
+                               ((MockPlatformServices)Device.PlatformServices).RuntimePlatform = Device.Android;
+                               p = new Bz44213(useCompiledXaml);
+                               p.BindingContext = new { Foo = "Foo", Bar = "Bar" };
+                               Assert.AreEqual("Bar", p.label.Text);
+                       }
+               }
+       }
+}
\ No newline at end of file
index 631d001..5649861 100644 (file)
     <Compile Include="Issues\Bz43733.xaml.cs">
       <DependentUpon>Bz43733.xaml</DependentUpon>
     </Compile>
+    <Compile Include="Issues\Bz44213.xaml.cs">
+      <DependentUpon>Bz44213.xaml</DependentUpon>
+    </Compile>
   </ItemGroup>
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
   <Import Project="..\.nuspec\Xamarin.Forms.Debug.targets" />
     <EmbeddedResource Include="Issues\Bz43733.xaml">
       <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
     </EmbeddedResource>
+    <EmbeddedResource Include="Issues\Bz44213.xaml">
+      <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
+    </EmbeddedResource>
   </ItemGroup>
   <ItemGroup>
     <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
index 879ea3b..47ef9f6 100644 (file)
@@ -361,7 +361,7 @@ namespace Xamarin.Forms.Xaml
                        exception = null;
 
                        var elementType = element.GetType();
-                       var binding = value as BindingBase;
+                       var binding = value.ConvertTo(typeof(BindingBase),pinfoRetriever:null,serviceProvider:null) as BindingBase;
                        var bindable = element as BindableObject;
                        var nativeBindingService = DependencyService.Get<INativeBindingService>();