summaryrefslogtreecommitdiff
path: root/Xamarin.Forms.Xaml
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 /Xamarin.Forms.Xaml
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
Diffstat (limited to 'Xamarin.Forms.Xaml')
-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
4 files changed, 264 insertions, 204 deletions
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)
{