summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStephane Delcroix <stephane@delcroix.org>2016-09-08 20:45:43 +0200
committerJason Smith <jason.smith@xamarin.com>2016-09-08 11:45:43 -0700
commit3b7d798fdda51a669683ed7d5c3770ebf3adfa77 (patch)
tree8e4d16d91e9a6cb5a49a8aecf8514a8c84f36b97
parent85426c5d9495eb1d55b3128bf97e50c68a73b53f (diff)
downloadxamarin-forms-3b7d798fdda51a669683ed7d5c3770ebf3adfa77.tar.gz
xamarin-forms-3b7d798fdda51a669683ed7d5c3770ebf3adfa77.tar.bz2
xamarin-forms-3b7d798fdda51a669683ed7d5c3770ebf3adfa77.zip
[Xaml] support native views and native bindings (#266)
Allows including Native views directly in xaml. Support for ios, android, UWP
-rw-r--r--Xamarin.Forms.Build.Tasks/XamlGTask.cs4
-rw-r--r--Xamarin.Forms.Build.Tasks/XmlTypeExtensions.cs3
-rw-r--r--Xamarin.Forms.Controls/CoreGallery.cs1
-rw-r--r--Xamarin.Forms.Controls/GalleryPages/XamlNativeViews.xaml14
-rw-r--r--Xamarin.Forms.Controls/GalleryPages/XamlNativeViews.xaml.cs21
-rw-r--r--Xamarin.Forms.Controls/Xamarin.Forms.Controls.csproj8
-rw-r--r--Xamarin.Forms.Core.UnitTests/NativeBindingTests.cs1
-rw-r--r--Xamarin.Forms.Core/INativeBindingService.cs10
-rw-r--r--Xamarin.Forms.Core/INativeValueConverterService.cs9
-rw-r--r--Xamarin.Forms.Core/Xamarin.Forms.Core.csproj4
-rw-r--r--Xamarin.Forms.Core/XamlParseException.cs2
-rw-r--r--Xamarin.Forms.Platform.Android/NativeBindingservice.cs39
-rw-r--r--Xamarin.Forms.Platform.Android/NativeValueConverterService.cs19
-rw-r--r--Xamarin.Forms.Platform.Android/Xamarin.Forms.Platform.Android.csproj2
-rw-r--r--Xamarin.Forms.Platform.UAP/NativeBindingService.cs47
-rw-r--r--Xamarin.Forms.Platform.UAP/NativeValueConverterService.cs29
-rw-r--r--Xamarin.Forms.Platform.UAP/Xamarin.Forms.Platform.UAP.csproj2
-rw-r--r--Xamarin.Forms.Platform.iOS/NativeBindingService.cs39
-rw-r--r--Xamarin.Forms.Platform.iOS/NativeValueConverterService.cs20
-rw-r--r--Xamarin.Forms.Platform.iOS/Xamarin.Forms.Platform.iOS.csproj4
-rw-r--r--Xamarin.Forms.Xaml.UnitTests/Issues/BPNotResolvedOnSubClass.xaml.cs13
-rw-r--r--Xamarin.Forms.Xaml.UnitTests/Issues/Issue1497.cs16
-rw-r--r--Xamarin.Forms.Xaml.UnitTests/NativeViewsAndBindings.xaml13
-rw-r--r--Xamarin.Forms.Xaml.UnitTests/NativeViewsAndBindings.xaml.cs293
-rw-r--r--Xamarin.Forms.Xaml.UnitTests/Xamarin.Forms.Xaml.UnitTests.csproj6
-rw-r--r--Xamarin.Forms.Xaml/ApplyPropertiesVisitor.cs365
-rw-r--r--Xamarin.Forms.Xaml/TypeConversionExtensions.cs15
-rw-r--r--Xamarin.Forms.Xaml/XamlParser.cs77
-rw-r--r--Xamarin.Forms.Xaml/XmlnsHelper.cs11
29 files changed, 873 insertions, 214 deletions
diff --git a/Xamarin.Forms.Build.Tasks/XamlGTask.cs b/Xamarin.Forms.Build.Tasks/XamlGTask.cs
index 72375d6b..79a49c46 100644
--- a/Xamarin.Forms.Build.Tasks/XamlGTask.cs
+++ b/Xamarin.Forms.Build.Tasks/XamlGTask.cs
@@ -90,8 +90,8 @@ namespace Xamarin.Forms.Build.Tasks
return;
}
- string rootAsm;
- XmlnsHelper.ParseXmlns(rootClass.Value, out rootType, out rootNs, out rootAsm);
+ string rootAsm, targetPlatform;
+ XmlnsHelper.ParseXmlns(rootClass.Value, out rootType, out rootNs, out rootAsm, out targetPlatform);
namesAndTypes = GetNamesAndTypes(root, nsmgr);
var typeArguments = root.Attributes["TypeArguments", "http://schemas.microsoft.com/winfx/2009/xaml"];
diff --git a/Xamarin.Forms.Build.Tasks/XmlTypeExtensions.cs b/Xamarin.Forms.Build.Tasks/XmlTypeExtensions.cs
index e0810633..52460418 100644
--- a/Xamarin.Forms.Build.Tasks/XmlTypeExtensions.cs
+++ b/Xamarin.Forms.Build.Tasks/XmlTypeExtensions.cs
@@ -36,8 +36,9 @@ namespace Xamarin.Forms.Build.Tasks
string ns;
string typename;
string asmstring;
+ string targetPlatform;
- XmlnsHelper.ParseXmlns(namespaceURI, out typename, out ns, out asmstring);
+ XmlnsHelper.ParseXmlns(namespaceURI, out typename, out ns, out asmstring, out targetPlatform);
asmstring = asmstring ?? module.Assembly.Name.Name;
lookupAssemblies.Add(new Tuple<string, string>(asmstring, ns));
}
diff --git a/Xamarin.Forms.Controls/CoreGallery.cs b/Xamarin.Forms.Controls/CoreGallery.cs
index 06f214b4..a7743400 100644
--- a/Xamarin.Forms.Controls/CoreGallery.cs
+++ b/Xamarin.Forms.Controls/CoreGallery.cs
@@ -222,6 +222,7 @@ namespace Xamarin.Forms.Controls
var pages = new List<Page> {
new PlatformSpecificsGallery() {Title = "Platform Specifics"},
new NativeBindingGalleryPage {Title = "Native Binding Controls Gallery"},
+ new XamlNativeViews {Title = "Xaml Native Views Gallery"},
new AppLinkPageGallery {Title = "App Link Page Gallery"},
new NestedNativeControlGalleryPage {Title = "Nested Native Controls Gallery"},
new CellForceUpdateSizeGalleryPage {Title = "Cell Force Update Size Gallery"},
diff --git a/Xamarin.Forms.Controls/GalleryPages/XamlNativeViews.xaml b/Xamarin.Forms.Controls/GalleryPages/XamlNativeViews.xaml
new file mode 100644
index 00000000..7a1fb850
--- /dev/null
+++ b/Xamarin.Forms.Controls/GalleryPages/XamlNativeViews.xaml
@@ -0,0 +1,14 @@
+<?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:ios="clr-namespace:UIKit;assembly=Xamarin.iOS;targetPlatform=iOS"
+ xmlns:androidWidget="clr-namespace:Android.Widget;assembly=Mono.Android;targetPlatform=Android"
+ xmlns:formsandroid="clr-namespace:Xamarin.Forms;assembly=Xamarin.Forms.Platform.Android;targetPlatform=Android"
+ xmlns:win="clr-namespace:Windows.UI.Xaml.Controls;assembly=Windows, Version=255.255.255.255, Culture=neutral, PublicKeyToken=null, ContentType=WindowsRuntime;targetPlatform=Windows"
+ x:Class="Xamarin.Forms.Controls.XamlNativeViews">
+ <ContentPage.Content>
+ <ios:UILabel Text="{Binding NativeText}" View.HorizontalOptions="Start"/>
+ <androidWidget:TextView Text="{Binding NativeText}" x:Arguments="{x:Static formsandroid:Forms.Context}" />
+ <win:TextBlock Text="Foo"/>
+ </ContentPage.Content>
+</ContentPage> \ No newline at end of file
diff --git a/Xamarin.Forms.Controls/GalleryPages/XamlNativeViews.xaml.cs b/Xamarin.Forms.Controls/GalleryPages/XamlNativeViews.xaml.cs
new file mode 100644
index 00000000..1e40fdb7
--- /dev/null
+++ b/Xamarin.Forms.Controls/GalleryPages/XamlNativeViews.xaml.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+
+using Xamarin.Forms;
+
+namespace Xamarin.Forms.Controls
+{
+ public partial class XamlNativeViews : ContentPage
+ {
+ public XamlNativeViews()
+ {
+ InitializeComponent();
+ BindingContext = new VM { NativeText = "Text set to Native view using native binding" };
+ }
+ }
+
+ public class VM
+ {
+ public string NativeText { get; set; }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Controls/Xamarin.Forms.Controls.csproj b/Xamarin.Forms.Controls/Xamarin.Forms.Controls.csproj
index 3b02adbe..7ad923e0 100644
--- a/Xamarin.Forms.Controls/Xamarin.Forms.Controls.csproj
+++ b/Xamarin.Forms.Controls/Xamarin.Forms.Controls.csproj
@@ -236,6 +236,9 @@
<Compile Include="ControlGalleryPages\AutomationIDGallery.cs" />
<Compile Include="GalleryPages\AppLinkPageGallery.cs" />
<Compile Include="ControlGalleryPages\NativeBindingGalleryPage.cs" />
+ <Compile Include="GalleryPages\XamlNativeViews.xaml.cs">
+ <DependentUpon>XamlNativeViews.xaml</DependentUpon>
+ </Compile>
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
<Import Project="..\.nuspec\Xamarin.Forms.targets" />
@@ -257,6 +260,9 @@
<EmbeddedResource Include="GalleryPages\StyleXamlGallery.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
+ <EmbeddedResource Include="GalleryPages\XamlNativeViews.xaml">
+ <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
+ </EmbeddedResource>
</ItemGroup>
<Import Project="..\Xamarin.Forms.Controls.Issues\Xamarin.Forms.Controls.Issues.Shared\Xamarin.Forms.Controls.Issues.Shared.projitems" Label="Shared" />
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
@@ -308,4 +314,4 @@
</CreateItem>
<Copy SourceFiles="@(ConfigFile)" DestinationFiles="controlgallery.config" Condition="!Exists('controlgallery.config')" />
</Target>
-</Project> \ No newline at end of file
+</Project>
diff --git a/Xamarin.Forms.Core.UnitTests/NativeBindingTests.cs b/Xamarin.Forms.Core.UnitTests/NativeBindingTests.cs
index e7c7f9a8..66577f63 100644
--- a/Xamarin.Forms.Core.UnitTests/NativeBindingTests.cs
+++ b/Xamarin.Forms.Core.UnitTests/NativeBindingTests.cs
@@ -52,7 +52,6 @@ namespace Xamarin.Forms.Core.UnitTests
NativeView.SetBindingContext(BindingContext, nv => nv.SubViews);
base.OnBindingContextChanged();
}
-
}
public class MockNativeColor
diff --git a/Xamarin.Forms.Core/INativeBindingService.cs b/Xamarin.Forms.Core/INativeBindingService.cs
new file mode 100644
index 00000000..d926dae4
--- /dev/null
+++ b/Xamarin.Forms.Core/INativeBindingService.cs
@@ -0,0 +1,10 @@
+namespace Xamarin.Forms.Xaml
+{
+
+ interface INativeBindingService
+ {
+ bool TrySetBinding(object target, string propertyName, BindingBase binding);
+ bool TrySetBinding(object target, BindableProperty property, BindingBase binding);
+ bool TrySetValue(object target, BindableProperty property, object value);
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/INativeValueConverterService.cs b/Xamarin.Forms.Core/INativeValueConverterService.cs
new file mode 100644
index 00000000..4309be9a
--- /dev/null
+++ b/Xamarin.Forms.Core/INativeValueConverterService.cs
@@ -0,0 +1,9 @@
+using System;
+
+namespace Xamarin.Forms.Xaml
+{
+ interface INativeValueConverterService
+ {
+ bool ConvertTo(object value, Type toType, out object nativeValue);
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/Xamarin.Forms.Core.csproj b/Xamarin.Forms.Core/Xamarin.Forms.Core.csproj
index 9e961521..a07b88ca 100644
--- a/Xamarin.Forms.Core/Xamarin.Forms.Core.csproj
+++ b/Xamarin.Forms.Core/Xamarin.Forms.Core.csproj
@@ -433,6 +433,8 @@
<Compile Include="ListStringTypeConverter.cs" />
<Compile Include="PoppedToRootEventArgs.cs" />
<Compile Include="NativeBindingHelpers.cs" />
+ <Compile Include="INativeValueConverterService.cs" />
+ <Compile Include="INativeBindingService.cs" />
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
<ItemGroup>
@@ -447,4 +449,4 @@
</PostBuildEvent>
</PropertyGroup>
<ItemGroup />
-</Project> \ No newline at end of file
+</Project>
diff --git a/Xamarin.Forms.Core/XamlParseException.cs b/Xamarin.Forms.Core/XamlParseException.cs
index 42e8b618..d953da0f 100644
--- a/Xamarin.Forms.Core/XamlParseException.cs
+++ b/Xamarin.Forms.Core/XamlParseException.cs
@@ -7,7 +7,7 @@ namespace Xamarin.Forms.Xaml
{
readonly string _unformattedMessage;
- public XamlParseException(string message, IXmlLineInfo xmlInfo) : base(FormatMessage(message, xmlInfo))
+ public XamlParseException(string message, IXmlLineInfo xmlInfo, Exception innerException = null) : base(FormatMessage(message, xmlInfo), innerException)
{
_unformattedMessage = message;
XmlInfo = xmlInfo;
diff --git a/Xamarin.Forms.Platform.Android/NativeBindingservice.cs b/Xamarin.Forms.Platform.Android/NativeBindingservice.cs
new file mode 100644
index 00000000..3dd4fe3c
--- /dev/null
+++ b/Xamarin.Forms.Platform.Android/NativeBindingservice.cs
@@ -0,0 +1,39 @@
+using System;
+using AView = Android.Views.View;
+
+[assembly: Xamarin.Forms.Dependency(typeof(Xamarin.Forms.Platform.Android.NativeBindingService))]
+
+namespace Xamarin.Forms.Platform.Android
+{
+ class NativeBindingService : Xaml.INativeBindingService
+ {
+ public bool TrySetBinding(object target, string propertyName, BindingBase binding)
+ {
+ var view = target as AView;
+ if (view == null)
+ return false;
+ if (target.GetType().GetProperty(propertyName)?.GetMethod == null)
+ return false;
+ view.SetBinding(propertyName, binding);
+ return true;
+ }
+
+ public bool TrySetBinding(object target, BindableProperty property, BindingBase binding)
+ {
+ var view = target as AView;
+ if (view == null)
+ return false;
+ view.SetBinding(property, binding);
+ return true;
+ }
+
+ public bool TrySetValue(object target, BindableProperty property, object value)
+ {
+ var view = target as AView;
+ if (view == null)
+ return false;
+ view.SetValue(property, value);
+ return true;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.Android/NativeValueConverterService.cs b/Xamarin.Forms.Platform.Android/NativeValueConverterService.cs
new file mode 100644
index 00000000..ff6faa90
--- /dev/null
+++ b/Xamarin.Forms.Platform.Android/NativeValueConverterService.cs
@@ -0,0 +1,19 @@
+using System;
+using AView = Android.Views.View;
+
+[assembly: Xamarin.Forms.Dependency(typeof(Xamarin.Forms.Platform.Android.NativeValueConverterService))]
+namespace Xamarin.Forms.Platform.Android
+{
+ class NativeValueConverterService : Xaml.INativeValueConverterService
+ {
+ public bool ConvertTo(object value, Type toType, out object nativeValue)
+ {
+ nativeValue = null;
+ if (typeof(AView).IsInstanceOfType(value) && toType.IsAssignableFrom(typeof(View))) {
+ nativeValue = ((AView)value).ToView();
+ return true;
+ }
+ return false;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.Android/Xamarin.Forms.Platform.Android.csproj b/Xamarin.Forms.Platform.Android/Xamarin.Forms.Platform.Android.csproj
index 00179717..2852b85b 100644
--- a/Xamarin.Forms.Platform.Android/Xamarin.Forms.Platform.Android.csproj
+++ b/Xamarin.Forms.Platform.Android/Xamarin.Forms.Platform.Android.csproj
@@ -242,6 +242,8 @@
<Compile Include="AndroidAppIndexProvider.cs" />
<Compile Include="Renderers\FormsSeekBar.cs" />
<Compile Include="Extensions\NativeBindingExtensions.cs" />
+ <Compile Include="NativeValueConverterService.cs" />
+ <Compile Include="NativeBindingservice.cs" />
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
diff --git a/Xamarin.Forms.Platform.UAP/NativeBindingService.cs b/Xamarin.Forms.Platform.UAP/NativeBindingService.cs
new file mode 100644
index 00000000..e999af2a
--- /dev/null
+++ b/Xamarin.Forms.Platform.UAP/NativeBindingService.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Windows.UI.Xaml;
+
+#if WINDOWS_UWP
+[assembly: Xamarin.Forms.Dependency(typeof(Xamarin.Forms.Platform.UWP.NativeBindingService))]
+namespace Xamarin.Forms.Platform.UWP
+#else
+[assembly: Xamarin.Forms.Dependency(typeof(Xamarin.Forms.Platform.WinRT.NativeBindingService))]
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public class NativeBindingService : Xaml.INativeBindingService
+ {
+ public bool TrySetBinding(object target, string propertyName, BindingBase binding)
+ {
+ var view = target as FrameworkElement;
+ if (view == null)
+ return false;
+ if (target.GetType().GetProperty(propertyName)?.GetMethod == null)
+ return false;
+ view.SetBinding(propertyName, binding);
+ return true;
+ }
+
+ public bool TrySetBinding(object target, BindableProperty property, BindingBase binding)
+ {
+ var view = target as FrameworkElement;
+ if (view == null)
+ return false;
+ view.SetBinding(property, binding);
+ return true;
+ }
+
+ public bool TrySetValue(object target, BindableProperty property, object value)
+ {
+ var view = target as FrameworkElement;
+ if (view == null)
+ return false;
+ view.SetValue(property, value);
+ return true;
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.UAP/NativeValueConverterService.cs b/Xamarin.Forms.Platform.UAP/NativeValueConverterService.cs
new file mode 100644
index 00000000..1f0ef68a
--- /dev/null
+++ b/Xamarin.Forms.Platform.UAP/NativeValueConverterService.cs
@@ -0,0 +1,29 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Windows.UI.Xaml;
+
+#if WINDOWS_UWP
+[assembly: Xamarin.Forms.Dependency(typeof(Xamarin.Forms.Platform.UWP.NativeValueConverterService))]
+namespace Xamarin.Forms.Platform.UWP
+#else
+[assembly: Xamarin.Forms.Dependency(typeof(Xamarin.Forms.Platform.WinRT.NativeValueConverterService))]
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public class NativeValueConverterService : Xaml.INativeValueConverterService
+ {
+ public bool ConvertTo(object value, Type toType, out object nativeValue)
+ {
+ nativeValue = null;
+ if (typeof(FrameworkElement).IsInstanceOfType(value) && toType.IsAssignableFrom(typeof(View)))
+ {
+ nativeValue = ((FrameworkElement)value).ToView();
+ return true;
+ }
+ return false;
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.UAP/Xamarin.Forms.Platform.UAP.csproj b/Xamarin.Forms.Platform.UAP/Xamarin.Forms.Platform.UAP.csproj
index 81ec3ff3..4f383064 100644
--- a/Xamarin.Forms.Platform.UAP/Xamarin.Forms.Platform.UAP.csproj
+++ b/Xamarin.Forms.Platform.UAP/Xamarin.Forms.Platform.UAP.csproj
@@ -189,6 +189,8 @@
<Link>LayoutExtensions.cs</Link>
</Compile>
<Compile Include="IToolBarForegroundBinder.cs" />
+ <Compile Include="NativeBindingService.cs" />
+ <Compile Include="NativeValueConverterService.cs" />
<Compile Include="SearchBarRenderer.cs" />
<Compile Include="..\Xamarin.Forms.Platform.WinRT\TextAlignmentToHorizontalAlignmentConverter.cs">
<Link>TextAlignmentToHorizontalAlignmentConverter.cs</Link>
diff --git a/Xamarin.Forms.Platform.iOS/NativeBindingService.cs b/Xamarin.Forms.Platform.iOS/NativeBindingService.cs
new file mode 100644
index 00000000..3756e2f0
--- /dev/null
+++ b/Xamarin.Forms.Platform.iOS/NativeBindingService.cs
@@ -0,0 +1,39 @@
+using System;
+using UIKit;
+
+[assembly: Xamarin.Forms.Dependency(typeof(Xamarin.Forms.Platform.iOS.NativeBindingService))]
+
+namespace Xamarin.Forms.Platform.iOS
+{
+ class NativeBindingService : Xaml.INativeBindingService
+ {
+ public bool TrySetBinding(object target, string propertyName, BindingBase binding)
+ {
+ var view = target as UIView;
+ if (view == null)
+ return false;
+ if (target.GetType().GetProperty(propertyName)?.GetMethod == null)
+ return false;
+ view.SetBinding(propertyName, binding);
+ return true;
+ }
+
+ public bool TrySetBinding(object target, BindableProperty property, BindingBase binding)
+ {
+ var view = target as UIView;
+ if (view == null)
+ return false;
+ view.SetBinding(property, binding);
+ return true;
+ }
+
+ public bool TrySetValue(object target, BindableProperty property, object value)
+ {
+ var view = target as UIView;
+ if (view == null)
+ return false;
+ view.SetValue(property, value);
+ return true;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.iOS/NativeValueConverterService.cs b/Xamarin.Forms.Platform.iOS/NativeValueConverterService.cs
new file mode 100644
index 00000000..9e20e045
--- /dev/null
+++ b/Xamarin.Forms.Platform.iOS/NativeValueConverterService.cs
@@ -0,0 +1,20 @@
+using System;
+using UIKit;
+
+[assembly: Xamarin.Forms.Dependency(typeof(Xamarin.Forms.Platform.iOS.NativeValueConverterService))]
+
+namespace Xamarin.Forms.Platform.iOS
+{
+ class NativeValueConverterService : Xaml.INativeValueConverterService
+ {
+ public bool ConvertTo(object value, Type toType, out object nativeValue)
+ {
+ nativeValue = null;
+ if (typeof(UIView).IsInstanceOfType(value) && toType.IsAssignableFrom(typeof(View))) {
+ nativeValue = ((UIView)value).ToView();
+ return true;
+ }
+ return false;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.iOS/Xamarin.Forms.Platform.iOS.csproj b/Xamarin.Forms.Platform.iOS/Xamarin.Forms.Platform.iOS.csproj
index 8fde4c81..e31aa3ac 100644
--- a/Xamarin.Forms.Platform.iOS/Xamarin.Forms.Platform.iOS.csproj
+++ b/Xamarin.Forms.Platform.iOS/Xamarin.Forms.Platform.iOS.csproj
@@ -150,6 +150,8 @@
<Compile Include="IOSAppLinks.cs" />
<Compile Include="NativeViewPropertyListener.cs" />
<Compile Include="Extensions\LayoutExtensions.cs" />
+ <Compile Include="NativeValueConverterService.cs" />
+ <Compile Include="NativeBindingService.cs" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\StringResources.ar.resx" />
@@ -205,4 +207,4 @@
<Link>Properties\GlobalAssemblyInfo.cs</Link>
</Compile>
</ItemGroup>
-</Project> \ No newline at end of file
+</Project>
diff --git a/Xamarin.Forms.Xaml.UnitTests/Issues/BPNotResolvedOnSubClass.xaml.cs b/Xamarin.Forms.Xaml.UnitTests/Issues/BPNotResolvedOnSubClass.xaml.cs
index 65bb73f1..c6913949 100644
--- a/Xamarin.Forms.Xaml.UnitTests/Issues/BPNotResolvedOnSubClass.xaml.cs
+++ b/Xamarin.Forms.Xaml.UnitTests/Issues/BPNotResolvedOnSubClass.xaml.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using Xamarin.Forms;
using NUnit.Framework;
+using Xamarin.Forms.Core.UnitTests;
namespace Xamarin.Forms.Xaml.UnitTests
{
@@ -29,6 +30,18 @@ namespace Xamarin.Forms.Xaml.UnitTests
[TestFixture]
class Tests
{
+ [SetUp]
+ public void Setup()
+ {
+ Device.PlatformServices = new MockPlatformServices();
+ }
+
+ [TearDown]
+ public void TearDown()
+ {
+ Device.PlatformServices = null;
+ }
+
[TestCase(true)]
[TestCase(false)]
public void CorrectlyResolveBPOnSubClasses (bool useCompiledXaml)
diff --git a/Xamarin.Forms.Xaml.UnitTests/Issues/Issue1497.cs b/Xamarin.Forms.Xaml.UnitTests/Issues/Issue1497.cs
index 3813f73a..b35339d5 100644
--- a/Xamarin.Forms.Xaml.UnitTests/Issues/Issue1497.cs
+++ b/Xamarin.Forms.Xaml.UnitTests/Issues/Issue1497.cs
@@ -1,11 +1,24 @@
using System;
using NUnit.Framework;
+using Xamarin.Forms.Core.UnitTests;
namespace Xamarin.Forms.Xaml.UnitTests
{
[TestFixture]
public class Issue1497
{
+ [SetUp]
+ public void Setup()
+ {
+ Device.PlatformServices = new MockPlatformServices();
+ }
+
+ [TearDown]
+ public void TearDown()
+ {
+ Device.PlatformServices = null;
+ }
+
[Test]
public void BPCollectionsWithSingleElement ()
{
@@ -23,5 +36,4 @@ namespace Xamarin.Forms.Xaml.UnitTests
Assert.True (grid.ColumnDefinitions [0].Width.IsStar);
}
}
-}
-
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Xaml.UnitTests/NativeViewsAndBindings.xaml b/Xamarin.Forms.Xaml.UnitTests/NativeViewsAndBindings.xaml
new file mode 100644
index 00000000..a35f8e50
--- /dev/null
+++ b/Xamarin.Forms.Xaml.UnitTests/NativeViewsAndBindings.xaml
@@ -0,0 +1,13 @@
+<?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:ios="clr-namespace:Xamarin.Forms.Xaml.UnitTests;targetPlatform=iOS"
+ xmlns:android="clr-namespace:Xamarin.Forms.Xaml.UnitTests;targetPlatform=Android"
+ x:Class="Xamarin.Forms.Xaml.UnitTests.NativeViewsAndBindings">
+ <StackLayout>
+ <ContentView x:Name="view0">
+ <ios:MockUIView Foo="foo" Bar="42" Baz="{Binding Baz}" View.HorizontalOptions="End" View.VerticalOptions="{Binding VerticalOption}" />
+ <android:MockAndroidView Foo="foo" Bar="42" Baz="{Binding Baz}" View.HorizontalOptions="End" View.VerticalOptions="{Binding VerticalOption}" />
+ </ContentView>
+ </StackLayout>
+</ContentPage> \ No newline at end of file
diff --git a/Xamarin.Forms.Xaml.UnitTests/NativeViewsAndBindings.xaml.cs b/Xamarin.Forms.Xaml.UnitTests/NativeViewsAndBindings.xaml.cs
new file mode 100644
index 00000000..ce02d63b
--- /dev/null
+++ b/Xamarin.Forms.Xaml.UnitTests/NativeViewsAndBindings.xaml.cs
@@ -0,0 +1,293 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using NUnit.Framework;
+using Xamarin.Forms;
+using Xamarin.Forms.Core.UnitTests;
+
+namespace Xamarin.Forms.Xaml.UnitTests
+{
+ public abstract class MockNativeView
+ {
+ public string Foo { get; set; }
+ public int Bar { get; set; }
+ public string Baz { get; set; }
+ }
+
+ public class MockUIView : MockNativeView
+ {
+ public IList<MockUIView> SubViews { get; set; }
+ }
+
+ class MockUIViewWrapper : View
+ {
+ public MockUIView NativeView { get; }
+
+ public MockUIViewWrapper(MockUIView nativeView)
+ {
+ NativeView = nativeView;
+ nativeView.TransferbindablePropertiesToWrapper(this);
+ }
+
+ protected override void OnBindingContextChanged()
+ {
+ NativeView.SetBindingContext(BindingContext, nv => nv.SubViews);
+ base.OnBindingContextChanged();
+ }
+ }
+
+ public class MockAndroidView : MockNativeView
+ {
+ public IList<MockAndroidView> SubViews { get; set; }
+ }
+
+ class MockAndroidViewWrapper : View
+ {
+ public MockAndroidView NativeView { get; }
+
+ public MockAndroidViewWrapper(MockAndroidView nativeView)
+ {
+ NativeView = nativeView;
+ nativeView.TransferbindablePropertiesToWrapper(this);
+ }
+
+ protected override void OnBindingContextChanged()
+ {
+ NativeView.SetBindingContext(BindingContext, nv => nv.SubViews);
+ base.OnBindingContextChanged();
+ }
+ }
+
+ public static class MockNativeViewExtensions
+ {
+ public static View ToView(this MockUIView nativeView)
+ {
+ return new MockUIViewWrapper(nativeView);
+ }
+
+ public static void SetBinding(this MockUIView target, string targetProperty, BindingBase binding, string updateSourceEventName = null)
+ {
+ NativeBindingHelpers.SetBinding(target, targetProperty, binding, updateSourceEventName);
+ }
+
+ internal static void SetBinding(this MockUIView target, string targetProperty, BindingBase binding, INotifyPropertyChanged propertyChanged)
+ {
+ NativeBindingHelpers.SetBinding(target, targetProperty, binding, propertyChanged);
+ }
+
+ public static void SetBinding(this MockUIView target, BindableProperty targetProperty, BindingBase binding)
+ {
+ NativeBindingHelpers.SetBinding(target, targetProperty, binding);
+ }
+
+ public static void SetValue(this MockUIView target, BindableProperty targetProperty, object value)
+ {
+ NativeBindingHelpers.SetValue(target, targetProperty, value);
+ }
+
+ public static void SetBindingContext(this MockUIView target, object bindingContext, Func<MockUIView, IEnumerable<MockUIView>> getChild = null)
+ {
+ NativeBindingHelpers.SetBindingContext(target, bindingContext, getChild);
+ }
+
+ internal static void TransferbindablePropertiesToWrapper(this MockUIView target, MockUIViewWrapper wrapper)
+ {
+ NativeBindingHelpers.TransferBindablePropertiesToWrapper(target, wrapper);
+ }
+
+ public static View ToView(this MockAndroidView nativeView)
+ {
+ return new MockAndroidViewWrapper(nativeView);
+ }
+
+ public static void SetBinding(this MockAndroidView target, string targetProperty, BindingBase binding, string updateSourceEventName = null)
+ {
+ NativeBindingHelpers.SetBinding(target, targetProperty, binding, updateSourceEventName);
+ }
+
+ internal static void SetBinding(this MockAndroidView target, string targetProperty, BindingBase binding, INotifyPropertyChanged propertyChanged)
+ {
+ NativeBindingHelpers.SetBinding(target, targetProperty, binding, propertyChanged);
+ }
+
+ public static void SetBinding(this MockAndroidView target, BindableProperty targetProperty, BindingBase binding)
+ {
+ NativeBindingHelpers.SetBinding(target, targetProperty, binding);
+ }
+
+ public static void SetValue(this MockAndroidView target, BindableProperty targetProperty, object value)
+ {
+ NativeBindingHelpers.SetValue(target, targetProperty, value);
+ }
+
+ public static void SetBindingContext(this MockAndroidView target, object bindingContext, Func<MockAndroidView, IEnumerable<MockAndroidView>> getChild = null)
+ {
+ NativeBindingHelpers.SetBindingContext(target, bindingContext, getChild);
+ }
+
+ internal static void TransferbindablePropertiesToWrapper(this MockAndroidView target, MockAndroidViewWrapper wrapper)
+ {
+ NativeBindingHelpers.TransferBindablePropertiesToWrapper(target, wrapper);
+ }
+ }
+
+ public class MockIosNativeValueConverterService : INativeValueConverterService
+ {
+ public bool ConvertTo(object value, Type toType, out object nativeValue)
+ {
+ nativeValue = null;
+ if (typeof(MockUIView).IsInstanceOfType(value) && toType.IsAssignableFrom(typeof(View))) {
+ nativeValue = ((MockUIView)value).ToView();
+ return true;
+ }
+ return false;
+ }
+ }
+
+ public class MockAndroidNativeValueConverterService : INativeValueConverterService
+ {
+ public bool ConvertTo(object value, Type toType, out object nativeValue)
+ {
+ nativeValue = null;
+ if (typeof(MockAndroidView).IsInstanceOfType(value) && toType.IsAssignableFrom(typeof(View))) {
+ nativeValue = ((MockAndroidView)value).ToView();
+ return true;
+ }
+ return false;
+ }
+ }
+
+ public class MockIosNativeBindingService : INativeBindingService
+ {
+ public bool TrySetBinding(object target, string propertyName, BindingBase binding)
+ {
+ var view = target as MockUIView;
+ if (view == null)
+ return false;
+ if (target.GetType().GetProperty(propertyName)?.GetMethod == null)
+ return false;
+ view.SetBinding(propertyName, binding);
+ return true;
+ }
+
+ public bool TrySetBinding(object target, BindableProperty property, BindingBase binding)
+ {
+ var view = target as MockUIView;
+ if (view == null)
+ return false;
+ view.SetBinding(property, binding);
+ return true;
+ }
+
+ public bool TrySetValue(object target, BindableProperty property, object value)
+ {
+ var view = target as MockUIView;
+ if (view == null)
+ return false;
+ view.SetValue(property, value);
+ return true;
+ }
+ }
+
+ public class MockAndroidNativeBindingService : INativeBindingService
+ {
+ public bool TrySetBinding(object target, string propertyName, BindingBase binding)
+ {
+ var view = target as MockAndroidView;
+ if (view == null)
+ return false;
+ view.SetBinding(propertyName, binding);
+ return true;
+ }
+
+ public bool TrySetBinding(object target, BindableProperty property, BindingBase binding)
+ {
+ var view = target as MockAndroidView;
+ if (view == null)
+ return false;
+ view.SetBinding(property, binding);
+ return true;
+ }
+
+ public bool TrySetValue(object target, BindableProperty property, object value)
+ {
+ var view = target as MockAndroidView;
+ if (view == null)
+ return false;
+ view.SetValue(property, value);
+ return true;
+ }
+ }
+
+ public partial class NativeViewsAndBindings : ContentPage
+ {
+ public NativeViewsAndBindings()
+ {
+ InitializeComponent();
+ }
+
+ public NativeViewsAndBindings(bool useCompiledXaml)
+ {
+ //this stub will be replaced at compile time
+ }
+
+ [TestFixture]
+ public class Tests
+ {
+ [SetUp]
+ public void SetUp()
+ {
+ Device.PlatformServices = new MockPlatformServices();
+ }
+
+ [TearDown]
+ public void TearDown()
+ {
+ Device.PlatformServices = null;
+ }
+
+ void SetUpPlatform(TargetPlatform platform)
+ {
+ Device.OS = platform;
+ if (platform == TargetPlatform.iOS) {
+ DependencyService.Register<INativeValueConverterService, MockIosNativeValueConverterService>();
+ DependencyService.Register<INativeBindingService, MockIosNativeBindingService>();
+ } else if (platform == TargetPlatform.Android) {
+ DependencyService.Register<INativeValueConverterService, MockAndroidNativeValueConverterService>();
+ DependencyService.Register<INativeBindingService, MockAndroidNativeBindingService>();
+ }
+ }
+
+ [TestCase(false, TargetPlatform.iOS)]
+ [TestCase(false, TargetPlatform.Android)]
+ //[TestCase(true)]
+ public void NativeInContentView(bool useCompiledXaml, TargetPlatform platform)
+ {
+ SetUpPlatform(platform);
+ var layout = new NativeViewsAndBindings(useCompiledXaml);
+ layout.BindingContext = new {
+ Baz = "Bound Value",
+ VerticalOption=LayoutOptions.EndAndExpand
+ };
+ var view = layout.view0;
+ Assert.NotNull(view.Content);
+ MockNativeView nativeView = null;
+ if (platform == TargetPlatform.iOS) {
+ Assert.That(view.Content, Is.TypeOf<MockUIViewWrapper>());
+ Assert.That(((MockUIViewWrapper)view.Content).NativeView, Is.TypeOf<MockUIView>());
+ nativeView = ((MockUIViewWrapper)view.Content).NativeView;
+ } else if (platform == TargetPlatform.Android) {
+ Assert.That(view.Content, Is.TypeOf<MockAndroidViewWrapper>());
+ Assert.That(((MockAndroidViewWrapper)view.Content).NativeView, Is.TypeOf<MockAndroidView>());
+ nativeView = ((MockAndroidViewWrapper)view.Content).NativeView;
+ }
+
+ Assert.AreEqual("foo", nativeView.Foo);
+ Assert.AreEqual(42, nativeView.Bar);
+ Assert.AreEqual("Bound Value", nativeView.Baz);
+ Assert.AreEqual(LayoutOptions.End, view.Content.GetValue(View.HorizontalOptionsProperty));
+ Assert.AreEqual(LayoutOptions.EndAndExpand, view.Content.GetValue(View.VerticalOptionsProperty));
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Xaml.UnitTests/Xamarin.Forms.Xaml.UnitTests.csproj b/Xamarin.Forms.Xaml.UnitTests/Xamarin.Forms.Xaml.UnitTests.csproj
index 86e2eeb0..26bf6d41 100644
--- a/Xamarin.Forms.Xaml.UnitTests/Xamarin.Forms.Xaml.UnitTests.csproj
+++ b/Xamarin.Forms.Xaml.UnitTests/Xamarin.Forms.Xaml.UnitTests.csproj
@@ -359,6 +359,9 @@
<Compile Include="TypeExtension.xaml.cs">
<DependentUpon>TypeExtension.xaml</DependentUpon>
</Compile>
+ <Compile Include="NativeViewsAndBindings.xaml.cs">
+ <DependentUpon>NativeViewsAndBindings.xaml</DependentUpon>
+ </Compile>
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<Import Project="..\.nuspec\Xamarin.Forms.Debug.targets" />
@@ -638,6 +641,9 @@
<EmbeddedResource Include="TypeExtension.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
+ <EmbeddedResource Include="NativeViewsAndBindings.xaml">
+ <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
+ </EmbeddedResource>
</ItemGroup>
<ItemGroup>
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
diff --git a/Xamarin.Forms.Xaml/ApplyPropertiesVisitor.cs b/Xamarin.Forms.Xaml/ApplyPropertiesVisitor.cs
index 327a4122..36f5b7fd 100644
--- a/Xamarin.Forms.Xaml/ApplyPropertiesVisitor.cs
+++ b/Xamarin.Forms.Xaml/ApplyPropertiesVisitor.cs
@@ -7,6 +7,8 @@ using System.Xml;
using Xamarin.Forms.Internals;
using Xamarin.Forms.Xaml.Internals;
+using static System.String;
+
namespace Xamarin.Forms.Xaml
{
internal class ApplyPropertiesVisitor : IXamlNodeVisitor
@@ -26,20 +28,17 @@ namespace Xamarin.Forms.Xaml
StopOnResourceDictionary = stopOnResourceDictionary;
}
- Dictionary<INode, object> Values
- {
+ Dictionary<INode, object> Values {
get { return Context.Values; }
}
HydratationContext Context { get; }
- public bool VisitChildrenFirst
- {
+ public bool VisitChildrenFirst {
get { return true; }
}
- public bool StopOnDataTemplate
- {
+ public bool StopOnDataTemplate {
get { return true; }
}
@@ -48,12 +47,11 @@ namespace Xamarin.Forms.Xaml
public void Visit(ValueNode node, INode parentNode)
{
var parentElement = parentNode as IElementNode;
- var value = Values[node];
- var source = Values[parentNode];
+ var value = Values [node];
+ var source = Values [parentNode];
XmlName propertyName;
- if (TryGetPropertyName(node, parentNode, out propertyName))
- {
+ if (TryGetPropertyName(node, parentNode, out propertyName)) {
if (Skips.Contains(propertyName))
return;
if (parentElement.SkipProperties.Contains(propertyName))
@@ -62,13 +60,10 @@ namespace Xamarin.Forms.Xaml
propertyName.LocalName == "Ignorable")
return;
SetPropertyValue(source, propertyName, value, Context.RootElement, node, Context, node);
- }
- else if (IsCollectionItem(node, parentNode) && parentNode is IElementNode)
- {
+ } else if (IsCollectionItem(node, parentNode) && parentNode is IElementNode) {
// Collection element, implicit content, or implicit collection element.
- var contentProperty = GetContentPropertyName(Context.Types[parentElement].GetTypeInfo());
- if (contentProperty != null)
- {
+ var contentProperty = GetContentPropertyName(Context.Types [parentElement].GetTypeInfo());
+ if (contentProperty != null) {
var name = new XmlName(((ElementNode)parentNode).NamespaceURI, contentProperty);
if (Skips.Contains(name))
return;
@@ -85,19 +80,17 @@ namespace Xamarin.Forms.Xaml
public void Visit(ElementNode node, INode parentNode)
{
- var value = Values[node];
+ var value = Values [node];
var parentElement = parentNode as IElementNode;
var markupExtension = value as IMarkupExtension;
var valueProvider = value as IValueProvider;
- if (markupExtension != null)
- {
+ if (markupExtension != null) {
var serviceProvider = new XamlServiceProvider(node, Context);
value = markupExtension.ProvideValue(serviceProvider);
}
- if (valueProvider != null)
- {
+ if (valueProvider != null) {
var serviceProvider = new XamlServiceProvider(node, Context);
value = valueProvider.ProvideValue(serviceProvider);
}
@@ -124,37 +117,29 @@ namespace Xamarin.Forms.Xaml
SetTemplate(source as ElementTemplate, node);
else
SetPropertyValue(source, propertyName, value, Context.RootElement, node, Context, node);
- }
- else if (IsCollectionItem(node, parentNode) && parentNode is IElementNode)
- {
+ } else if (IsCollectionItem(node, parentNode) && parentNode is IElementNode) {
// Collection element, implicit content, or implicit collection element.
string contentProperty;
- if (typeof (IEnumerable).GetTypeInfo().IsAssignableFrom(Context.Types[parentElement].GetTypeInfo()))
- {
- var source = Values[parentNode];
- if (!(typeof (ResourceDictionary).IsAssignableFrom(Context.Types[parentElement])))
- {
+ if (typeof(IEnumerable).GetTypeInfo().IsAssignableFrom(Context.Types [parentElement].GetTypeInfo())) {
+ var source = Values [parentNode];
+ if (!(typeof(ResourceDictionary).IsAssignableFrom(Context.Types [parentElement]))) {
var addMethod =
- Context.Types[parentElement].GetRuntimeMethods().First(mi => mi.Name == "Add" && mi.GetParameters().Length == 1);
- addMethod.Invoke(source, new[] { value });
+ Context.Types [parentElement].GetRuntimeMethods().First(mi => mi.Name == "Add" && mi.GetParameters().Length == 1);
+ addMethod.Invoke(source, new [] { value });
}
- }
- else if ((contentProperty = GetContentPropertyName(Context.Types[parentElement].GetTypeInfo())) != null)
- {
+ } else if ((contentProperty = GetContentPropertyName(Context.Types [parentElement].GetTypeInfo())) != null) {
var name = new XmlName(node.NamespaceURI, contentProperty);
if (Skips.Contains(name))
return;
if (parentElement.SkipProperties.Contains(propertyName))
return;
- var source = Values[parentNode];
+ var source = Values [parentNode];
SetPropertyValue(source, name, value, Context.RootElement, node, Context, node);
}
- }
- else if (IsCollectionItem(node, parentNode) && parentNode is ListNode)
- {
+ } else if (IsCollectionItem(node, parentNode) && parentNode is ListNode) {
var parentList = (ListNode)parentNode;
- var source = Values[parentNode.Parent];
+ var source = Values [parentNode.Parent];
if (Skips.Contains(parentList.XmlName))
return;
@@ -165,15 +150,11 @@ namespace Xamarin.Forms.Xaml
GetRealNameAndType(ref elementType, parentList.XmlName.NamespaceURI, ref localname, Context, node);
PropertyInfo propertyInfo = null;
- try
- {
+ try {
propertyInfo = elementType.GetRuntimeProperty(localname);
- }
- catch (AmbiguousMatchException)
- {
+ } catch (AmbiguousMatchException) {
// Get most derived instance of property
- foreach (var property in elementType.GetRuntimeProperties().Where(prop => prop.Name == localname))
- {
+ foreach (var property in elementType.GetRuntimeProperties().Where(prop => prop.Name == localname)) {
if (propertyInfo == null || propertyInfo.DeclaringType.IsAssignableFrom(property.DeclaringType))
propertyInfo = property;
}
@@ -184,7 +165,7 @@ namespace Xamarin.Forms.Xaml
if (!propertyInfo.CanRead || (getter = propertyInfo.GetMethod) == null)
throw new XamlParseException(string.Format("Property {0} does not have an accessible getter", localname), node);
IEnumerable collection;
- if ((collection = getter.Invoke(source, new object[] { }) as IEnumerable) == null)
+ if ((collection = getter.Invoke(source, new object [] { }) as IEnumerable) == null)
throw new XamlParseException(string.Format("Property {0} is null or is not IEnumerable", localname), node);
MethodInfo addMethod;
if (
@@ -192,7 +173,7 @@ namespace Xamarin.Forms.Xaml
collection.GetType().GetRuntimeMethods().First(mi => mi.Name == "Add" && mi.GetParameters().Length == 1)) == null)
throw new XamlParseException(string.Format("Value of {0} does not have a Add() method", localname), node);
- addMethod.Invoke(collection, new[] { Values[node] });
+ addMethod.Invoke(collection, new [] { Values [node] });
}
}
@@ -210,8 +191,7 @@ namespace Xamarin.Forms.Xaml
var parentElement = parentNode as IElementNode;
if (parentElement == null)
return false;
- foreach (var kvp in parentElement.Properties)
- {
+ foreach (var kvp in parentElement.Properties) {
if (kvp.Value != node)
continue;
name = kvp.Key;
@@ -230,8 +210,7 @@ namespace Xamarin.Forms.Xaml
internal static string GetContentPropertyName(TypeInfo typeInfo)
{
- while (typeInfo != null)
- {
+ while (typeInfo != null) {
var propName = GetContentPropertyName(typeInfo.CustomAttributes);
if (propName != null)
return propName;
@@ -246,8 +225,8 @@ namespace Xamarin.Forms.Xaml
attributes.FirstOrDefault(cad => ContentPropertyAttribute.ContentPropertyTypes.Contains(cad.AttributeType.FullName));
if (contentAttribute == null || contentAttribute.ConstructorArguments.Count != 1)
return null;
- if (contentAttribute.ConstructorArguments[0].ArgumentType == typeof (string))
- return (string)contentAttribute.ConstructorArguments[0].Value;
+ if (contentAttribute.ConstructorArguments [0].ArgumentType == typeof(string))
+ return (string)contentAttribute.ConstructorArguments [0].Value;
return null;
}
@@ -255,8 +234,7 @@ namespace Xamarin.Forms.Xaml
HydratationContext context, IXmlLineInfo lineInfo)
{
var dotIdx = localname.IndexOf('.');
- if (dotIdx > 0)
- {
+ if (dotIdx > 0) {
var typename = localname.Substring(0, dotIdx);
localname = localname.Substring(dotIdx + 1);
XamlParseException xpe;
@@ -276,8 +254,7 @@ namespace Xamarin.Forms.Xaml
elementType.GetFields().FirstOrDefault(fi => fi.Name == localName + "Property" && fi.IsStatic && fi.IsPublic);
Exception exception = null;
- if (exception == null && bindableFieldInfo == null)
- {
+ if (exception == null && bindableFieldInfo == null) {
exception =
new XamlParseException(
string.Format("BindableProperty {0} not found on {1}", localName + "Property", elementType.Name), lineInfo);
@@ -290,148 +267,204 @@ namespace Xamarin.Forms.Xaml
return null;
}
- public static void SetPropertyValue(object xamlelement, XmlName propertyName, object value, object rootElement,
- INode node, HydratationContext context, IXmlLineInfo lineInfo)
+ public static void SetPropertyValue(object xamlelement, XmlName propertyName, object value, object rootElement, INode node, HydratationContext context, IXmlLineInfo lineInfo)
{
- var elementType = xamlelement.GetType();
- var localname = propertyName.LocalName;
-
+ var localName = propertyName.LocalName;
var serviceProvider = new XamlServiceProvider(node, context);
+ Exception xpe = null;
//If it's an attached BP, update elementType and propertyName
- var attached = GetRealNameAndType(ref elementType, propertyName.NamespaceURI, ref localname, context, lineInfo);
+ var bpOwnerType = xamlelement.GetType();
+ var attached = GetRealNameAndType(ref bpOwnerType, propertyName.NamespaceURI, ref localName, context, lineInfo);
+ var property = GetBindableProperty(bpOwnerType, localName, lineInfo, false);
//If the target is an event, connect
- var eventInfo = elementType.GetRuntimeEvent(localname);
- if (eventInfo != null && value is string)
- {
- var methodInfo = rootElement.GetType().GetRuntimeMethods().FirstOrDefault(mi => mi.Name == (string)value);
- if (methodInfo == null) {
- var xpe = new XamlParseException (string.Format ("No method {0} found on type {1}", value, rootElement.GetType ()), lineInfo);
- if (context.DoNotThrowOnExceptions) {
- System.Diagnostics.Debug.WriteLine (xpe.Message);
- return;
- } else
- throw xpe;
- }
- try
- {
- eventInfo.AddEventHandler(xamlelement, methodInfo.CreateDelegate(eventInfo.EventHandlerType, rootElement));
- }
- catch (ArgumentException)
- {
- var xpe = new XamlParseException (string.Format ("Method {0} does not have the correct signature", value), lineInfo);
- if (context.DoNotThrowOnExceptions)
- System.Diagnostics.Debug.WriteLine (xpe.Message);
- else
- throw xpe;
- }
+ if (xpe == null && TryConnectEvent(xamlelement, localName, value, rootElement, lineInfo, out xpe))
+ return;
+ //If Value is DynamicResource and it's a BP, SetDynamicResource
+ if (xpe == null && TrySetDynamicResource(xamlelement, property, value, lineInfo, out xpe))
return;
- }
- var property = GetBindableProperty(elementType, localname, lineInfo, false);
+ //If value is BindingBase, SetBinding
+ if (xpe == null && TrySetBinding(xamlelement, property, localName, value, lineInfo, out xpe))
+ return;
- //If Value is DynamicResource and it's a BP, SetDynamicResource
- if (value is DynamicResource && property != null)
- {
- if (!(xamlelement.GetType()).GetTypeInfo().IsSubclassOf(typeof (BindableObject)))
- throw new XamlParseException(string.Format("{0} is not a BindableObject", xamlelement.GetType().Name), lineInfo);
- ((BindableObject)xamlelement).SetDynamicResource(property, ((DynamicResource)value).Key);
+ //If it's a BindableProberty, SetValue
+ if (xpe == null && TrySetValue(xamlelement, property, attached, value, lineInfo, serviceProvider, out xpe))
return;
- }
- //If value is BindingBase, and target is a BindableProperty, SetBinding
- if (value is BindingBase && property != null)
- {
- if (!(xamlelement.GetType()).GetTypeInfo().IsSubclassOf(typeof (BindableObject)))
- throw new XamlParseException(string.Format("{0} is not a BindableObject", xamlelement.GetType().Name), lineInfo);
+ //If we can assign that value to a normal property, let's do it
+ if (xpe == null && TrySetProperty(xamlelement, localName, value, lineInfo, serviceProvider, out xpe))
+ return;
- ((BindableObject)xamlelement).SetBinding(property, value as BindingBase);
+ //If it's an already initialized property, add to it
+ if (xpe == null && TryAddToProperty(xamlelement, localName, value, lineInfo, serviceProvider, out xpe))
return;
+
+ xpe = xpe ?? new XamlParseException($"Cannot assign property \"{localName}\": Property does not exists, or is not assignable, or mismatching type between value and property", lineInfo);
+ if (context.DoNotThrowOnExceptions)
+ System.Diagnostics.Debug.WriteLine(xpe.Message);
+ else
+ throw xpe;
+ }
+
+ static bool TryConnectEvent(object element, string localName, object value, object rootElement, IXmlLineInfo lineInfo, out Exception exception)
+ {
+ exception = null;
+
+ var elementType = element.GetType();
+ var eventInfo = elementType.GetRuntimeEvent(localName);
+ var stringValue = value as string;
+
+ if (eventInfo == null || IsNullOrEmpty(stringValue))
+ return false;
+
+ var methodInfo = rootElement.GetType().GetRuntimeMethods().FirstOrDefault(mi => mi.Name == (string)value);
+ if (methodInfo == null) {
+ exception = new XamlParseException($"No method {value} found on type {rootElement.GetType()}", lineInfo);
+ return false;
}
- //If it's a BindableProberty, SetValue
+ try {
+ eventInfo.AddEventHandler(element, methodInfo.CreateDelegate(eventInfo.EventHandlerType, rootElement));
+ return true;
+ } catch (ArgumentException ae) {
+ exception = new XamlParseException($"Method {stringValue} does not have the correct signature", lineInfo, ae);
+ }
+ return false;
+ }
+
+ static bool TrySetDynamicResource(object element, BindableProperty property, object value, IXmlLineInfo lineInfo, out Exception exception)
+ {
+ exception = null;
+
+ var elementType = element.GetType();
+ var dynamicResource = value as DynamicResource;
+ var bindable = element as BindableObject;
+
+ if (dynamicResource == null || property == null)
+ return false;
+
+ if (bindable == null) {
+ exception = new XamlParseException($"{elementType.Name} is not a BindableObject", lineInfo);
+ return false;
+ }
+
+ bindable.SetDynamicResource(property, dynamicResource.Key);
+ return true;
+ }
+
+ static bool TrySetBinding(object element, BindableProperty property, string localName, object value, IXmlLineInfo lineInfo, out Exception exception)
+ {
+ exception = null;
+
+ var elementType = element.GetType();
+ var binding = value as BindingBase;
+ var bindable = element as BindableObject;
+ var nativeBindingService = DependencyService.Get<INativeBindingService>();
+
+ if (binding == null)
+ return false;
+
+ if (bindable != null && property != null) {
+ bindable.SetBinding(property, binding);
+ return true;
+ }
+
+ if (nativeBindingService != null && property != null && nativeBindingService.TrySetBinding(element, property, binding))
+ return true;
+
+ if (nativeBindingService != null && nativeBindingService.TrySetBinding(element, localName, binding))
+ return true;
+
if (property != null)
- {
- if (!(xamlelement.GetType()).GetTypeInfo().IsSubclassOf(typeof (BindableObject)))
- throw new XamlParseException(string.Format("{0} is not a BindableObject", xamlelement.GetType().Name), lineInfo);
- Func<MemberInfo> minforetriever;
- if (attached)
- minforetriever = () => elementType.GetRuntimeMethod("Get" + localname, new[] { typeof (BindableObject) });
- else
- minforetriever = () => elementType.GetRuntimeProperty(localname);
+ exception = new XamlParseException($"{elementType.Name} is not a BindableObject or does not support native bindings", lineInfo);
- var convertedValue = value.ConvertTo(property.ReturnType, minforetriever, serviceProvider);
+ return false;
+ }
+ static bool TrySetValue(object element, BindableProperty property, bool attached, object value, IXmlLineInfo lineInfo, XamlServiceProvider serviceProvider, out Exception exception)
+ {
+ exception = null;
+
+ var elementType = element.GetType();
+ var bindable = element as BindableObject;
+ var nativeBindingService = DependencyService.Get<INativeBindingService>();
+
+ if (property == null)
+ return false;
+
+ Func<MemberInfo> minforetriever;
+ if (attached)
+ minforetriever = () => property.DeclaringType.GetRuntimeMethod("Get" + property.PropertyName, new [] { typeof(BindableObject) });
+ else
+ minforetriever = () => property.DeclaringType.GetRuntimeProperty(property.PropertyName);
+ var convertedValue = value.ConvertTo(property.ReturnType, minforetriever, serviceProvider);
+
+ if (bindable != null) {
//SetValue doesn't throw on mismatching type, so check before to get a chance to try the property setting or the collection adding
var nullable = property.ReturnTypeInfo.IsGenericType &&
- property.ReturnTypeInfo.GetGenericTypeDefinition() == typeof (Nullable<>);
+ property.ReturnTypeInfo.GetGenericTypeDefinition() == typeof(Nullable<>);
if ((convertedValue == null && (!property.ReturnTypeInfo.IsValueType || nullable)) ||
- (property.ReturnType.IsInstanceOfType(convertedValue)))
- {
- ((BindableObject)xamlelement).SetValue(property, convertedValue);
- return;
+ (property.ReturnType.IsInstanceOfType(convertedValue))) {
+ bindable.SetValue(property, convertedValue);
+ return true;
}
+ return false;
}
- var exception = new XamlParseException(
- String.Format("No Property of name {0} found", propertyName.LocalName), lineInfo);
+ if (nativeBindingService != null && nativeBindingService.TrySetValue(element, property, convertedValue))
+ return true;
- //If we can assign that value to a normal property, let's do it
- var propertyInfo = elementType.GetRuntimeProperties().FirstOrDefault(p => p.Name == localname);
+ exception = new XamlParseException($"{elementType.Name} is not a BindableObject or does not support setting native BindableProperties", lineInfo);
+ return false;
+ }
+
+ static bool TrySetProperty(object element, string localName, object value, IXmlLineInfo lineInfo, XamlServiceProvider serviceProvider, out Exception exception)
+ {
+ exception = null;
+
+ var elementType = element.GetType();
+ var propertyInfo = elementType.GetRuntimeProperties().FirstOrDefault(p => p.Name == localName);
MethodInfo setter;
- if (propertyInfo != null && propertyInfo.CanWrite && (setter = propertyInfo.SetMethod) != null)
- {
- object convertedValue = value.ConvertTo(propertyInfo.PropertyType, () => propertyInfo, serviceProvider);
- if (convertedValue == null || propertyInfo.PropertyType.IsInstanceOfType(convertedValue))
- {
- try
- {
- setter.Invoke(xamlelement, new[] { convertedValue });
- return;
- }
- catch (ArgumentException)
- {
- }
- }
- else
- {
- exception = new XamlParseException(
- String.Format("Cannot assign property \"{0}\": type mismatch between \"{1}\" and \"{2}\"", propertyName.LocalName,
- value.GetType(), propertyInfo.PropertyType), lineInfo);
- }
- }
+ if (propertyInfo == null || !propertyInfo.CanWrite || (setter = propertyInfo.SetMethod) == null)
+ return false;
- //If it's an already initialized property, add to it
+ object convertedValue = value.ConvertTo(propertyInfo.PropertyType, () => propertyInfo, serviceProvider);
+ if (convertedValue != null && !propertyInfo.PropertyType.IsInstanceOfType(convertedValue))
+ return false;
+
+ setter.Invoke(element, new object [] { convertedValue });
+ return true;
+ }
+
+ static bool TryAddToProperty(object element, string localName, object value, IXmlLineInfo lineInfo, XamlServiceProvider serviceProvider, out Exception exception)
+ {
+ exception = null;
+
+ var elementType = element.GetType();
+ var propertyInfo = elementType.GetRuntimeProperties().FirstOrDefault(p => p.Name == localName);
MethodInfo getter;
- if (propertyInfo != null && propertyInfo.CanRead && (getter = propertyInfo.GetMethod) != null)
- {
- IEnumerable collection;
- MethodInfo addMethod;
- if ((collection = getter.Invoke(xamlelement, new object[] { }) as IEnumerable) != null
- &&
- (addMethod =
- collection.GetType().GetRuntimeMethods().First(mi => mi.Name == "Add" && mi.GetParameters().Length == 1)) !=
- null)
- {
- addMethod.Invoke(collection,
- new[] { value.ConvertTo(addMethod.GetParameters()[0].ParameterType, (Func<TypeConverter>)null, serviceProvider) });
- return;
- }
- }
+ if (propertyInfo == null || !propertyInfo.CanRead || (getter = propertyInfo.GetMethod) == null)
+ return false;
- if (context.DoNotThrowOnExceptions)
- System.Diagnostics.Debug.WriteLine (exception.Message);
- else
- throw exception;
+ var collection = getter.Invoke(element, new object [] { }) as IEnumerable;
+ if (collection == null)
+ return false;
+
+ var addMethod = collection.GetType().GetRuntimeMethods().First(mi => mi.Name == "Add" && mi.GetParameters().Length == 1);
+ if (addMethod == null)
+ return false;
+
+ addMethod.Invoke(collection, new [] { value.ConvertTo(addMethod.GetParameters() [0].ParameterType, (Func<TypeConverter>)null, serviceProvider) });
+ return true;
}
void SetTemplate(ElementTemplate dt, INode node)
{
#pragma warning disable 0612
- ((IDataTemplate)dt).LoadTemplate = () =>
- {
+ ((IDataTemplate)dt).LoadTemplate = () => {
#pragma warning restore 0612
var cnode = node.Clone();
var context = new HydratationContext { ParentContext = Context, RootElement = Context.RootElement };
@@ -442,7 +475,7 @@ namespace Xamarin.Forms.Xaml
cnode.Accept(new RegisterXNamesVisitor(context), null);
cnode.Accept(new FillResourceDictionariesVisitor(context), null);
cnode.Accept(new ApplyPropertiesVisitor(context, true), null);
- return context.Values[cnode];
+ return context.Values [cnode];
};
}
}
diff --git a/Xamarin.Forms.Xaml/TypeConversionExtensions.cs b/Xamarin.Forms.Xaml/TypeConversionExtensions.cs
index a81ed11d..7e377ea4 100644
--- a/Xamarin.Forms.Xaml/TypeConversionExtensions.cs
+++ b/Xamarin.Forms.Xaml/TypeConversionExtensions.cs
@@ -1,5 +1,5 @@
//
-// InternalExtensions.cs
+// TypeConversionExtensions.cs
//
// Author:
// Stephane Delcroix <stephane@mi8.be>
@@ -153,9 +153,18 @@ namespace Xamarin.Forms.Xaml
if (value != null)
{
var cast = value.GetType().GetRuntimeMethod("op_Implicit", new[] { value.GetType() });
- if (cast != null && cast.ReturnType == toType)
- value = cast.Invoke(null, new[] { value });
+ if (cast != null && cast.ReturnType == toType) {
+ value = cast.Invoke(null, new [] { value });
+ return value;
+ }
}
+
+ var nativeValueConverterService = DependencyService.Get<INativeValueConverterService>();
+
+ object nativeValue = null;
+ if (nativeValueConverterService != null && nativeValueConverterService.ConvertTo(value, toType, out nativeValue))
+ return nativeValue;
+
return value;
}
}
diff --git a/Xamarin.Forms.Xaml/XamlParser.cs b/Xamarin.Forms.Xaml/XamlParser.cs
index 7dd79d77..60424754 100644
--- a/Xamarin.Forms.Xaml/XamlParser.cs
+++ b/Xamarin.Forms.Xaml/XamlParser.cs
@@ -38,7 +38,10 @@ namespace Xamarin.Forms.Xaml
{
public static void ParseXaml(RootNode rootNode, XmlReader reader)
{
- var attributes = ParseXamlAttributes(reader);
+ IList<KeyValuePair<string, string>> xmlns;
+ var attributes = ParseXamlAttributes(reader, out xmlns);
+ var prefixes = PrefixesToIgnore(xmlns);
+ (rootNode.IgnorablePrefixes ?? (rootNode.IgnorablePrefixes=new List<string>())).AddRange(prefixes);
rootNode.Properties.AddRange(attributes);
ParseXamlElementFor(rootNode, reader);
}
@@ -136,8 +139,10 @@ namespace Xamarin.Forms.Xaml
var elementName = reader.Name;
var elementNsUri = reader.NamespaceURI;
var elementXmlInfo = (IXmlLineInfo)reader;
+ IList<KeyValuePair<string, string>> xmlns;
- var attributes = ParseXamlAttributes(reader);
+ var attributes = ParseXamlAttributes(reader, out xmlns);
+ var prefixes = PrefixesToIgnore(xmlns);
IList<XmlType> typeArguments = null;
if (attributes.Any(kvp => kvp.Key == XmlName.xTypeArguments))
@@ -149,6 +154,7 @@ namespace Xamarin.Forms.Xaml
node = new ElementNode(new XmlType(elementNsUri, elementName, typeArguments), elementNsUri,
reader as IXmlNamespaceResolver, elementXmlInfo.LineNumber, elementXmlInfo.LinePosition);
((IElementNode)node).Properties.AddRange(attributes);
+ (node.IgnorablePrefixes ?? (node.IgnorablePrefixes = new List<string>())).AddRange(prefixes);
ParseXamlElementFor((IElementNode)node, reader);
nodes.Add(node);
@@ -170,17 +176,20 @@ namespace Xamarin.Forms.Xaml
throw new XamlParseException("Closing PropertyElement expected", (IXmlLineInfo)reader);
}
- static IList<KeyValuePair<XmlName, INode>> ParseXamlAttributes(XmlReader reader)
+ static IList<KeyValuePair<XmlName, INode>> ParseXamlAttributes(XmlReader reader, out IList<KeyValuePair<string,string>> xmlns)
{
Debug.Assert(reader.NodeType == XmlNodeType.Element);
var attributes = new List<KeyValuePair<XmlName, INode>>();
+ xmlns = new List<KeyValuePair<string, string>>();
for (var i = 0; i < reader.AttributeCount; i++)
{
reader.MoveToAttribute(i);
//skip xmlns
- if (reader.NamespaceURI == "http://www.w3.org/2000/xmlns/")
+ if (reader.NamespaceURI == "http://www.w3.org/2000/xmlns/") {
+ xmlns.Add(new KeyValuePair<string, string>(reader.LocalName, reader.Value));
continue;
+ }
var propertyName = new XmlName(reader.NamespaceURI, reader.LocalName);
@@ -239,6 +248,23 @@ namespace Xamarin.Forms.Xaml
return attributes;
}
+ static IList<string> PrefixesToIgnore(IList<KeyValuePair<string, string>> xmlns)
+ {
+ var prefixes = new List<string>();
+ foreach (var kvp in xmlns) {
+ var prefix = kvp.Key;
+
+ string typeName = null, ns = null, asm = null, targetPlatform = null;
+ XmlnsHelper.ParseXmlns(kvp.Value, out typeName, out ns, out asm, out targetPlatform);
+ if (targetPlatform == null)
+ continue;
+ TargetPlatform os;
+ if (Enum.TryParse<TargetPlatform>(targetPlatform, out os) && os != Device.OS)
+ prefixes.Add(prefix);
+ }
+ return prefixes;
+ }
+
static IValueNode GetValueNode(object value, XmlReader reader)
{
var valueString = value as string;
@@ -264,31 +290,26 @@ namespace Xamarin.Forms.Xaml
var typeArguments = xmlType.TypeArguments;
exception = null;
- List<Tuple<string, Assembly>> lookupAssemblies = new List<Tuple<string, Assembly>>();
- List<string> lookupNames = new List<string>();
+ var lookupAssemblies = new List<Tuple<string, string>>(); //namespace, assemblyqualifiednamed
+ var lookupNames = new List<string>();
if (!XmlnsHelper.IsCustom(namespaceURI))
{
- lookupAssemblies.Add(new Tuple<string, Assembly>("Xamarin.Forms", typeof (View).GetTypeInfo().Assembly));
- lookupAssemblies.Add(new Tuple<string, Assembly>("Xamarin.Forms.Xaml", typeof (XamlLoader).GetTypeInfo().Assembly));
+ lookupAssemblies.Add(new Tuple<string, string>("Xamarin.Forms", typeof (View).GetTypeInfo().Assembly.FullName));
+ lookupAssemblies.Add(new Tuple<string, string>("Xamarin.Forms.Xaml", typeof (XamlLoader).GetTypeInfo().Assembly.FullName));
}
else if (namespaceURI == "http://schemas.microsoft.com/winfx/2009/xaml" ||
namespaceURI == "http://schemas.microsoft.com/winfx/2006/xaml")
{
- lookupAssemblies.Add(new Tuple<string, Assembly>("Xamarin.Forms.Xaml", typeof (XamlLoader).GetTypeInfo().Assembly));
- lookupAssemblies.Add(new Tuple<string, Assembly>("System", typeof (object).GetTypeInfo().Assembly));
- lookupAssemblies.Add(new Tuple<string, Assembly>("System", typeof (Uri).GetTypeInfo().Assembly)); //System.dll
+ lookupAssemblies.Add(new Tuple<string, string>("Xamarin.Forms.Xaml", typeof (XamlLoader).GetTypeInfo().Assembly.FullName));
+ lookupAssemblies.Add(new Tuple<string, string>("System", typeof (object).GetTypeInfo().Assembly.FullName)); //mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+ lookupAssemblies.Add(new Tuple<string, string>("System", typeof (Uri).GetTypeInfo().Assembly.FullName)); //System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
}
else
{
- string ns;
- string typename;
- string asmstring;
- Assembly asm;
-
- XmlnsHelper.ParseXmlns(namespaceURI, out typename, out ns, out asmstring);
- asm = asmstring == null ? currentAssembly : Assembly.Load(new AssemblyName(asmstring));
- lookupAssemblies.Add(new Tuple<string, Assembly>(ns, asm));
+ string ns, asmstring, _;
+ XmlnsHelper.ParseXmlns(namespaceURI, out _, out ns, out asmstring, out _);
+ lookupAssemblies.Add(new Tuple<string, string>(ns, asmstring ?? currentAssembly.FullName));
}
lookupNames.Add(elementName);
@@ -305,16 +326,12 @@ namespace Xamarin.Forms.Xaml
}
Type type = null;
- foreach (var asm in lookupAssemblies)
- {
- if (type != null)
- break;
+ foreach (var asm in lookupAssemblies) {
foreach (var name in lookupNames)
- {
- if (type != null)
+ if ((type = Type.GetType($"{asm.Item1}.{name}, {asm.Item2}")) != null)
break;
- type = asm.Item2.GetType(asm.Item1 + "." + name);
- }
+ if (type != null)
+ break;
}
if (type != null && typeArguments != null)
@@ -340,11 +357,7 @@ namespace Xamarin.Forms.Xaml
}
if (type == null)
- {
- exception = new XamlParseException(string.Format("Type {0} not found in xmlns {1}", elementName, namespaceURI),
- xmlInfo);
- return null;
- }
+ exception = new XamlParseException($"Type {elementName} not found in xmlns {namespaceURI}", xmlInfo);
return type;
}
diff --git a/Xamarin.Forms.Xaml/XmlnsHelper.cs b/Xamarin.Forms.Xaml/XmlnsHelper.cs
index 778d2947..e3e37de4 100644
--- a/Xamarin.Forms.Xaml/XmlnsHelper.cs
+++ b/Xamarin.Forms.Xaml/XmlnsHelper.cs
@@ -20,15 +20,16 @@ namespace Xamarin.Forms.Xaml
string typeName;
string ns;
string asm;
+ string targetPlatform;
- ParseXmlns(xmlns, out typeName, out ns, out asm);
+ ParseXmlns(xmlns, out typeName, out ns, out asm, out targetPlatform);
return ns;
}
- public static void ParseXmlns(string xmlns, out string typeName, out string ns, out string asm)
+ public static void ParseXmlns(string xmlns, out string typeName, out string ns, out string asm, out string targetPlatform)
{
- typeName = ns = asm = null;
+ typeName = ns = asm = targetPlatform = null;
foreach (var decl in xmlns.Split(';'))
{
@@ -42,6 +43,10 @@ namespace Xamarin.Forms.Xaml
asm = decl.Substring(9, decl.Length - 9);
continue;
}
+ if (decl.StartsWith("targetPlatform=", StringComparison.Ordinal)) {
+ targetPlatform = decl.Substring(15, decl.Length - 15);
+ continue;
+ }
var nsind = decl.LastIndexOf(".", StringComparison.Ordinal);
if (nsind > 0)
{