Allows including Native views directly in xaml.
Support for ios, android, UWP
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"];
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));
}
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"},
--- /dev/null
+<?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
--- /dev/null
+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
<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" />
<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')" />
</CreateItem>
<Copy SourceFiles="@(ConfigFile)" DestinationFiles="controlgallery.config" Condition="!Exists('controlgallery.config')" />
</Target>
-</Project>
\ No newline at end of file
+</Project>
NativeView.SetBindingContext(BindingContext, nv => nv.SubViews);
base.OnBindingContextChanged();
}
-
}
public class MockNativeColor
--- /dev/null
+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
--- /dev/null
+using System;
+
+namespace Xamarin.Forms.Xaml
+{
+ interface INativeValueConverterService
+ {
+ bool ConvertTo(object value, Type toType, out object nativeValue);
+ }
+}
\ No newline at end of file
<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>
</PostBuildEvent>
</PropertyGroup>
<ItemGroup />
-</Project>
\ No newline at end of file
+</Project>
{
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;
--- /dev/null
+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
--- /dev/null
+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
<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.
--- /dev/null
+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;
+ }
+ }
+}
--- /dev/null
+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;
+ }
+ }
+}
<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>
--- /dev/null
+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
--- /dev/null
+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
<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" />
<Link>Properties\GlobalAssemblyInfo.cs</Link>
</Compile>
</ItemGroup>
-</Project>
\ No newline at end of file
+</Project>
using Xamarin.Forms;
using NUnit.Framework;
+using Xamarin.Forms.Core.UnitTests;
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)
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 ()
{
Assert.True (grid.ColumnDefinitions [0].Width.IsStar);
}
}
-}
-
+}
\ No newline at end of file
--- /dev/null
+<?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
--- /dev/null
+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
<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" />
<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}" />
using Xamarin.Forms.Internals;
using Xamarin.Forms.Xaml.Internals;
+using static System.String;
+
namespace Xamarin.Forms.Xaml
{
internal class ApplyPropertiesVisitor : IXamlNodeVisitor
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; }
}
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))
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;
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);
}
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;
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;
}
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 (
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] });
}
}
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;
internal static string GetContentPropertyName(TypeInfo typeInfo)
{
- while (typeInfo != null)
- {
+ while (typeInfo != null) {
var propName = GetContentPropertyName(typeInfo.CustomAttributes);
if (propName != null)
return propName;
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;
}
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;
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);
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 };
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];
};
}
}
//
-// InternalExtensions.cs
+// TypeConversionExtensions.cs
//
// Author:
// Stephane Delcroix <stephane@mi8.be>
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;
}
}
{
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);
}
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))
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);
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);
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;
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);
}
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)
}
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;
}
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(';'))
{
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)
{