summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStephane Delcroix <stephane@delcroix.org>2016-09-26 22:29:47 +0200
committerJason Smith <jason.smith@xamarin.com>2016-09-26 13:29:47 -0700
commit55f066584c507ec92d5054fac4f3a35f54c05522 (patch)
tree358ec7ed9a74e008b3bc601a9b391bcd193fb86b
parente6a20ddedb6c8dee989b4ac19c6e83cecafe9f29 (diff)
downloadxamarin-forms-55f066584c507ec92d5054fac4f3a35f54c05522.tar.gz
xamarin-forms-55f066584c507ec92d5054fac4f3a35f54c05522.tar.bz2
xamarin-forms-55f066584c507ec92d5054fac4f3a35f54c05522.zip
[XamlC] Compiled converters (#358)
-rw-r--r--Xamarin.Forms.Build.Tasks/CompiledConverters/BindablePropertyConverter.cs84
-rw-r--r--Xamarin.Forms.Build.Tasks/CompiledConverters/BindingTypeConverter.cs32
-rw-r--r--Xamarin.Forms.Build.Tasks/CompiledConverters/BoundsTypeConverter.cs73
-rw-r--r--Xamarin.Forms.Build.Tasks/CompiledConverters/ColorTypeConverter.cs55
-rw-r--r--Xamarin.Forms.Build.Tasks/CompiledConverters/ICompiledTypeConverter.cs12
-rw-r--r--Xamarin.Forms.Build.Tasks/CompiledConverters/LayoutOptionsConverter.cs37
-rw-r--r--Xamarin.Forms.Build.Tasks/CompiledConverters/RectangleTypeConverter.cs49
-rw-r--r--Xamarin.Forms.Build.Tasks/CreateObjectVisitor.cs15
-rw-r--r--Xamarin.Forms.Build.Tasks/ExpandMarkupsVisitor.cs2
-rw-r--r--Xamarin.Forms.Build.Tasks/NodeILExtensions.cs15
-rw-r--r--Xamarin.Forms.Build.Tasks/SetPropertiesVisitor.cs14
-rw-r--r--Xamarin.Forms.Build.Tasks/Xamarin.Forms.Build.Tasks.csproj10
-rw-r--r--Xamarin.Forms.Build.Tasks/XmlTypeExtensions.cs5
-rw-r--r--Xamarin.Forms.Core.UnitTests/ButtonUnitTest.cs2
-rw-r--r--Xamarin.Forms.Core/BindablePropertyConverter.cs15
-rw-r--r--Xamarin.Forms.Core/BindingTypeConverter.cs1
-rw-r--r--Xamarin.Forms.Core/BoundsTypeConverter.cs5
-rw-r--r--Xamarin.Forms.Core/ColorTypeConverter.cs72
-rw-r--r--Xamarin.Forms.Core/LayoutOptionsConverter.cs24
-rw-r--r--Xamarin.Forms.Core/ParameterAttribute.cs2
-rw-r--r--Xamarin.Forms.Core/ProvideCompiledAttribute.cs15
-rw-r--r--Xamarin.Forms.Core/RectangleTypeConverter.cs1
-rw-r--r--Xamarin.Forms.Core/TypeConverter.cs2
-rw-r--r--Xamarin.Forms.Core/Xamarin.Forms.Core.csproj1
-rw-r--r--Xamarin.Forms.Xaml.UnitTests/CompiledTypeConverter.xaml9
-rw-r--r--Xamarin.Forms.Xaml.UnitTests/CompiledTypeConverter.xaml.cs45
-rw-r--r--Xamarin.Forms.Xaml.UnitTests/Validation/SetterOnNonBP.xaml.cs6
-rw-r--r--Xamarin.Forms.Xaml.UnitTests/Xamarin.Forms.Xaml.UnitTests.csproj6
-rw-r--r--Xamarin.Forms.Xaml/CreateValuesVisitor.cs3
-rw-r--r--Xamarin.Forms.Xaml/XamlNode.cs3
30 files changed, 534 insertions, 81 deletions
diff --git a/Xamarin.Forms.Build.Tasks/CompiledConverters/BindablePropertyConverter.cs b/Xamarin.Forms.Build.Tasks/CompiledConverters/BindablePropertyConverter.cs
new file mode 100644
index 00000000..92a53539
--- /dev/null
+++ b/Xamarin.Forms.Build.Tasks/CompiledConverters/BindablePropertyConverter.cs
@@ -0,0 +1,84 @@
+using System.Collections.Generic;
+using System.Linq;
+using Mono.Cecil;
+using Mono.Cecil.Cil;
+
+using Xamarin.Forms.Build.Tasks;
+using Xamarin.Forms.Xaml;
+
+using static System.String;
+
+namespace Xamarin.Forms.Core.XamlC
+{
+ class BindablePropertyConverter : ICompiledTypeConverter
+ {
+ public IEnumerable<Instruction> ConvertFromString(string value, ModuleDefinition module, BaseNode node)
+ {
+ if (IsNullOrEmpty(value)) {
+ yield return Instruction.Create(OpCodes.Ldnull);
+ yield break;
+ }
+
+ FieldReference bpRef = null;
+ string typeName = null, propertyName = null;
+
+ var parts = value.Split('.');
+ if (parts.Length == 1) {
+ var parent = node.Parent?.Parent as IElementNode;
+ if ((node.Parent as ElementNode)?.XmlType.NamespaceUri == "http://xamarin.com/schemas/2014/forms" && (node.Parent as ElementNode)?.XmlType.Name == "Setter") {
+ if (parent.XmlType.NamespaceUri == "http://xamarin.com/schemas/2014/forms" &&
+ (parent.XmlType.Name == "Trigger" || parent.XmlType.Name == "DataTrigger" || parent.XmlType.Name == "MultiTrigger" || parent.XmlType.Name == "Style")) {
+ var ttnode = (parent as ElementNode).Properties [new XmlName("", "TargetType")];
+ if (ttnode is ValueNode)
+ typeName = (ttnode as ValueNode).Value as string;
+ else if (ttnode is IElementNode)
+ typeName = ((ttnode as IElementNode).CollectionItems.FirstOrDefault() as ValueNode)?.Value as string ?? ((ttnode as IElementNode).Properties [new XmlName("", "TypeName")] as ValueNode)?.Value as string;
+ }
+ } else if ((node.Parent as ElementNode)?.XmlType.NamespaceUri == "http://xamarin.com/schemas/2014/forms" && (node.Parent as ElementNode)?.XmlType.Name == "Trigger")
+ typeName = ((node.Parent as ElementNode).Properties [new XmlName("", "TargetType")] as ValueNode).Value as string;
+ propertyName = parts [0];
+ } else if (parts.Length == 2) {
+ typeName = parts [0];
+ propertyName = parts [1];
+ } else
+ throw new XamlParseException($"Cannot convert \"{value}\" into {typeof(BindableProperty)}", node);
+
+ var typeRef = GetTypeReference(typeName, module, node);
+ if (typeRef == null)
+ throw new XamlParseException($"Can't resolve {typeName}", node);
+ bpRef = GetBindablePropertyFieldReference(typeRef, propertyName, module);
+ if (bpRef == null)
+ throw new XamlParseException($"Can't resolve {propertyName} on {typeRef.Name}", node);
+ yield return Instruction.Create(OpCodes.Ldsfld, bpRef);
+ }
+
+ public static TypeReference GetTypeReference(string xmlType, ModuleDefinition module, BaseNode iNode)
+ {
+ var split = xmlType.Split(':');
+ if (split.Length > 2)
+ throw new XamlParseException($"Type \"{xmlType}\" is invalid", iNode);
+
+ string prefix, name;
+ if (split.Length == 2) {
+ prefix = split [0];
+ name = split [1];
+ } else {
+ prefix = "";
+ name = split [0];
+ }
+ var namespaceuri = iNode.NamespaceResolver.LookupNamespace(prefix) ?? "";
+ return XmlTypeExtensions.GetTypeReference(namespaceuri, name, module, iNode);
+ }
+
+ public static FieldReference GetBindablePropertyFieldReference(TypeReference typeRef, string propertyName, ModuleDefinition module)
+ {
+ TypeReference declaringTypeReference;
+ FieldReference bpRef = typeRef.GetField(fd => fd.Name == $"{propertyName}Property" && fd.IsStatic && fd.IsPublic, out declaringTypeReference);
+ if (bpRef != null) {
+ bpRef = module.Import(bpRef.ResolveGenericParameters(declaringTypeReference));
+ bpRef.FieldType = module.Import(bpRef.FieldType);
+ }
+ return bpRef;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Build.Tasks/CompiledConverters/BindingTypeConverter.cs b/Xamarin.Forms.Build.Tasks/CompiledConverters/BindingTypeConverter.cs
new file mode 100644
index 00000000..74990a86
--- /dev/null
+++ b/Xamarin.Forms.Build.Tasks/CompiledConverters/BindingTypeConverter.cs
@@ -0,0 +1,32 @@
+using System.Collections.Generic;
+using System.Linq;
+
+using Mono.Cecil;
+using Mono.Cecil.Cil;
+
+using Xamarin.Forms.Xaml;
+
+using static System.String;
+
+namespace Xamarin.Forms.Core.XamlC
+{
+ class BindingTypeConverter : ICompiledTypeConverter
+ {
+ public IEnumerable<Instruction> ConvertFromString(string value, ModuleDefinition module, BaseNode node)
+ {
+ if (IsNullOrEmpty(value))
+ throw new XamlParseException($"Cannot convert \"{value}\" into {typeof(Binding)}", node);
+
+ var bindingCtor = module.Import(typeof(Binding)).Resolve().Methods.FirstOrDefault(md => md.IsConstructor && md.Parameters.Count == 6);
+ var bindingCtorRef = module.Import(bindingCtor);
+
+ yield return Instruction.Create(OpCodes.Ldstr, value);
+ yield return Instruction.Create(OpCodes.Ldc_I4, (int)BindingMode.Default);
+ yield return Instruction.Create(OpCodes.Ldnull);
+ yield return Instruction.Create(OpCodes.Ldnull);
+ yield return Instruction.Create(OpCodes.Ldnull);
+ yield return Instruction.Create(OpCodes.Ldnull);
+ yield return Instruction.Create(OpCodes.Newobj, bindingCtorRef);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Build.Tasks/CompiledConverters/BoundsTypeConverter.cs b/Xamarin.Forms.Build.Tasks/CompiledConverters/BoundsTypeConverter.cs
new file mode 100644
index 00000000..1cc64fc5
--- /dev/null
+++ b/Xamarin.Forms.Build.Tasks/CompiledConverters/BoundsTypeConverter.cs
@@ -0,0 +1,73 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+
+using Mono.Cecil;
+using Mono.Cecil.Cil;
+
+using Xamarin.Forms.Xaml;
+
+namespace Xamarin.Forms.Core.XamlC
+{
+ class BoundsTypeConverter : ICompiledTypeConverter
+ {
+ IEnumerable<Instruction> ICompiledTypeConverter.ConvertFromString(string value, ModuleDefinition module, BaseNode node)
+ {
+ if (string.IsNullOrEmpty(value))
+ throw new XamlParseException($"Cannot convert \"{value}\" into {typeof(Rectangle)}", node);
+
+ double x = -1, y = -1, w = -1, h = -1;
+ bool hasX, hasY, hasW, hasH;
+ var xywh = value.Split(',');
+
+ if (xywh.Length != 2 && xywh.Length != 4)
+ throw new XamlParseException($"Cannot convert \"{value}\" into {typeof(Rectangle)}", node);
+
+ hasX = (xywh.Length == 2 || xywh.Length == 4) && double.TryParse(xywh [0], NumberStyles.Number, CultureInfo.InvariantCulture, out x);
+ hasY = (xywh.Length == 2 || xywh.Length == 4) && double.TryParse(xywh [1], NumberStyles.Number, CultureInfo.InvariantCulture, out y);
+ hasW = xywh.Length == 4 && double.TryParse(xywh [2], NumberStyles.Number, CultureInfo.InvariantCulture, out w);
+ hasH = xywh.Length == 4 && double.TryParse(xywh [3], NumberStyles.Number, CultureInfo.InvariantCulture, out h);
+
+ if (!hasW && xywh.Length == 4 && string.Compare("AutoSize", xywh [2].Trim(), StringComparison.OrdinalIgnoreCase) == 0) {
+ hasW = true;
+ w = AbsoluteLayout.AutoSize;
+ }
+
+ if (!hasH && xywh.Length == 4 && string.Compare("AutoSize", xywh [3].Trim(), StringComparison.OrdinalIgnoreCase) == 0) {
+ hasH = true;
+ h = AbsoluteLayout.AutoSize;
+ }
+
+ if (hasX && hasY && xywh.Length == 2) {
+ hasW = true;
+ w = AbsoluteLayout.AutoSize;
+ hasH = true;
+ h = AbsoluteLayout.AutoSize;
+ }
+
+ if (!hasX || !hasY || !hasW || !hasH)
+ throw new XamlParseException($"Cannot convert \"{value}\" into {typeof(Rectangle)}", node);
+
+ return GenerateIL(x, y, w, h, module);
+ }
+
+ IEnumerable<Instruction> GenerateIL(double x, double y, double w, double h, ModuleDefinition module)
+ {
+// IL_0000: ldc.r8 3.1000000000000001
+// IL_0009: ldc.r8 4.2000000000000002
+// IL_0012: ldc.r8 5.2999999999999998
+// IL_001b: ldc.r8 6.4000000000000004
+// IL_0024: newobj instance void valuetype Test.Rectangle::'.ctor'(float64, float64, float64, float64)
+
+ yield return Instruction.Create(OpCodes.Ldc_R8, x);
+ yield return Instruction.Create(OpCodes.Ldc_R8, y);
+ yield return Instruction.Create(OpCodes.Ldc_R8, w);
+ yield return Instruction.Create(OpCodes.Ldc_R8, h);
+
+ var rectangleCtor = module.Import(typeof(Rectangle)).Resolve().Methods.FirstOrDefault(md => md.IsConstructor && md.Parameters.Count == 4);
+ var rectangleCtorRef = module.Import(rectangleCtor);
+ yield return Instruction.Create(OpCodes.Newobj, rectangleCtorRef);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Build.Tasks/CompiledConverters/ColorTypeConverter.cs b/Xamarin.Forms.Build.Tasks/CompiledConverters/ColorTypeConverter.cs
new file mode 100644
index 00000000..39527954
--- /dev/null
+++ b/Xamarin.Forms.Build.Tasks/CompiledConverters/ColorTypeConverter.cs
@@ -0,0 +1,55 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+using Mono.Cecil;
+using Mono.Cecil.Cil;
+
+using Xamarin.Forms.Xaml;
+
+namespace Xamarin.Forms.Core.XamlC
+{
+ class ColorTypeConverter : ICompiledTypeConverter
+ {
+ public IEnumerable<Instruction> ConvertFromString(string value, ModuleDefinition module, BaseNode node)
+ {
+ do {
+ if (string.IsNullOrEmpty(value))
+ break;
+
+ value = value.Trim();
+
+ if (value.StartsWith("#", StringComparison.Ordinal)) {
+ var color = Color.FromHex(value);
+ yield return Instruction.Create(OpCodes.Ldc_R8, color.R);
+ yield return Instruction.Create(OpCodes.Ldc_R8, color.G);
+ yield return Instruction.Create(OpCodes.Ldc_R8, color.B);
+ yield return Instruction.Create(OpCodes.Ldc_R8, color.A);
+ var colorCtor = module.Import(typeof(Color)).Resolve().Methods.FirstOrDefault(
+ md => md.IsConstructor && md.Parameters.Count == 4 &&
+ md.Parameters.All(p => p.ParameterType.FullName == "System.Double"));
+ var colorCtorRef = module.Import(colorCtor);
+ yield return Instruction.Create(OpCodes.Newobj, colorCtorRef);
+ yield break;
+ }
+ var parts = value.Split('.');
+ if (parts.Length == 1 || (parts.Length == 2 && parts [0] == "Color")) {
+ var color = parts [parts.Length - 1];
+
+ var field = module.Import(typeof(Color)).Resolve().Fields.SingleOrDefault(fd => fd.Name == color && fd.IsStatic);
+ if (field != null) {
+ yield return Instruction.Create(OpCodes.Ldsfld, module.Import(field));
+ yield break;
+ }
+ var propertyGetter = module.Import(typeof(Color)).Resolve().Properties.SingleOrDefault(pd => pd.Name == color && pd.GetMethod.IsStatic)?.GetMethod;
+ if (propertyGetter != null) {
+ yield return Instruction.Create(OpCodes.Call, module.Import(propertyGetter));
+ yield break;
+ }
+ }
+ } while (false);
+
+ throw new XamlParseException($"Cannot convert \"{value}\" into {typeof(Color)}", node);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Build.Tasks/CompiledConverters/ICompiledTypeConverter.cs b/Xamarin.Forms.Build.Tasks/CompiledConverters/ICompiledTypeConverter.cs
new file mode 100644
index 00000000..5ed88d0a
--- /dev/null
+++ b/Xamarin.Forms.Build.Tasks/CompiledConverters/ICompiledTypeConverter.cs
@@ -0,0 +1,12 @@
+using System.Collections.Generic;
+using Mono.Cecil.Cil;
+using Mono.Cecil;
+using System.Xml;
+
+namespace Xamarin.Forms.Xaml
+{
+ interface ICompiledTypeConverter
+ {
+ IEnumerable<Instruction> ConvertFromString(string value, ModuleDefinition module, BaseNode node);
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Build.Tasks/CompiledConverters/LayoutOptionsConverter.cs b/Xamarin.Forms.Build.Tasks/CompiledConverters/LayoutOptionsConverter.cs
new file mode 100644
index 00000000..e252ed9d
--- /dev/null
+++ b/Xamarin.Forms.Build.Tasks/CompiledConverters/LayoutOptionsConverter.cs
@@ -0,0 +1,37 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+using Mono.Cecil;
+using Mono.Cecil.Cil;
+
+using Xamarin.Forms.Xaml;
+
+namespace Xamarin.Forms.Core.XamlC
+{
+ class LayoutOptionsConverter : ICompiledTypeConverter
+ {
+ public IEnumerable<Instruction> ConvertFromString(string value, ModuleDefinition module, BaseNode node)
+ {
+ do {
+ if (string.IsNullOrEmpty(value))
+ break;
+
+ value = value.Trim();
+
+ var parts = value.Split('.');
+ if (parts.Length == 1 || (parts.Length == 2 && parts [0] == "LayoutOptions")) {
+ var options = parts [parts.Length - 1];
+
+ var field = module.Import(typeof(LayoutOptions)).Resolve().Fields.SingleOrDefault(fd => fd.Name == options && fd.IsStatic);
+ if (field != null) {
+ yield return Instruction.Create(OpCodes.Ldsfld, module.Import(field));
+ yield break;
+ }
+ }
+ } while (false);
+
+ throw new XamlParseException(String.Format("Cannot convert \"{0}\" into {1}", value, typeof(LayoutOptions)), node);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Build.Tasks/CompiledConverters/RectangleTypeConverter.cs b/Xamarin.Forms.Build.Tasks/CompiledConverters/RectangleTypeConverter.cs
new file mode 100644
index 00000000..af480667
--- /dev/null
+++ b/Xamarin.Forms.Build.Tasks/CompiledConverters/RectangleTypeConverter.cs
@@ -0,0 +1,49 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+
+using Mono.Cecil;
+using Mono.Cecil.Cil;
+
+using Xamarin.Forms.Xaml;
+
+namespace Xamarin.Forms.Core.XamlC
+{
+ class RectangleTypeConverter : ICompiledTypeConverter
+ {
+ public IEnumerable<Instruction> ConvertFromString(string value, ModuleDefinition module, BaseNode node)
+ {
+ if (string.IsNullOrEmpty(value))
+ throw new XamlParseException($"Cannot convert \"{value}\" into {typeof(Rectangle)}", node);
+ double x, y, w, h;
+ var xywh = value.Split(',');
+ if (xywh.Length != 4 ||
+ !double.TryParse(xywh [0], NumberStyles.Number, CultureInfo.InvariantCulture, out x) ||
+ !double.TryParse(xywh [1], NumberStyles.Number, CultureInfo.InvariantCulture, out y) ||
+ !double.TryParse(xywh [2], NumberStyles.Number, CultureInfo.InvariantCulture, out w) ||
+ !double.TryParse(xywh [3], NumberStyles.Number, CultureInfo.InvariantCulture, out h))
+ throw new XamlParseException($"Cannot convert \"{value}\" into {typeof(Rectangle)}", node);
+
+ return GenerateIL(x, y, w, h, module);
+ }
+
+ IEnumerable<Instruction> GenerateIL(double x, double y, double w, double h, ModuleDefinition module)
+ {
+// IL_0000: ldc.r8 3.1000000000000001
+// IL_0009: ldc.r8 4.2000000000000002
+// IL_0012: ldc.r8 5.2999999999999998
+// IL_001b: ldc.r8 6.4000000000000004
+// IL_0024: newobj instance void valuetype Test.Rectangle::'.ctor'(float64, float64, float64, float64)
+
+ yield return Instruction.Create(OpCodes.Ldc_R8, x);
+ yield return Instruction.Create(OpCodes.Ldc_R8, y);
+ yield return Instruction.Create(OpCodes.Ldc_R8, w);
+ yield return Instruction.Create(OpCodes.Ldc_R8, h);
+
+ var rectangleCtor = module.Import(typeof(Rectangle)).Resolve().Methods.FirstOrDefault(md => md.IsConstructor && md.Parameters.Count == 4);
+ var rectangleCtorRef = module.Import(rectangleCtor);
+ yield return Instruction.Create(OpCodes.Newobj, rectangleCtorRef);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Build.Tasks/CreateObjectVisitor.cs b/Xamarin.Forms.Build.Tasks/CreateObjectVisitor.cs
index 449b887f..33a9a7f5 100644
--- a/Xamarin.Forms.Build.Tasks/CreateObjectVisitor.cs
+++ b/Xamarin.Forms.Build.Tasks/CreateObjectVisitor.cs
@@ -121,7 +121,7 @@ namespace Xamarin.Forms.Build.Tasks
}
if (parameterizedCtorInfo != null && ValidateCtorArguments(parameterizedCtorInfo, node)) {
ctorInfo = parameterizedCtorInfo;
- // IL_0000: ldstr "foo"
+// IL_0000: ldstr "foo"
Context.IL.Append(PushCtorArguments(parameterizedCtorInfo, node));
}
ctorInfo = ctorInfo ?? typedef.Methods.FirstOrDefault(md => md.IsConstructor && !md.HasParameters && !md.IsStatic);
@@ -198,9 +198,9 @@ namespace Xamarin.Forms.Build.Tasks
type = type.Contains(":") ? type.Split(':') [1].Trim() : type;
Context.TypeExtensions [node] = new XmlType(namespaceuri, type, null).GetTypeReference(Module, node);
- node.Properties.Clear();
- node.CollectionItems.Clear();
-
+ if (!node.SkipProperties.Contains(new XmlName("", "TypeName")))
+ node.SkipProperties.Add(new XmlName("", "TypeName"));
+
var vardefref = new VariableDefinitionReference(vardef);
Context.IL.Append(SetPropertiesVisitor.ProvideValue(vardefref, Context, Module, node));
if (vardef != vardefref.VariableDefinition) {
@@ -213,8 +213,8 @@ namespace Xamarin.Forms.Build.Tasks
public void Visit(RootNode node, INode parentNode)
{
- // IL_0013: ldarg.0
- // IL_0014: stloc.3
+// IL_0013: ldarg.0
+// IL_0014: stloc.3
var ilnode = (ILRootNode)node;
var typeref = ilnode.TypeReference;
@@ -256,7 +256,8 @@ namespace Xamarin.Forms.Build.Tasks
.ConstructorArguments.First()
.Value as string;
var node = enode.Properties[new XmlName("", propname)];
- enode.Properties.Remove(new XmlName("", propname));
+ if (!enode.SkipProperties.Contains(new XmlName("", propname)))
+ enode.SkipProperties.Add(new XmlName("", propname));
VariableDefinition vardef;
ValueNode vnode = null;
diff --git a/Xamarin.Forms.Build.Tasks/ExpandMarkupsVisitor.cs b/Xamarin.Forms.Build.Tasks/ExpandMarkupsVisitor.cs
index feafe214..26e241ab 100644
--- a/Xamarin.Forms.Build.Tasks/ExpandMarkupsVisitor.cs
+++ b/Xamarin.Forms.Build.Tasks/ExpandMarkupsVisitor.cs
@@ -49,6 +49,8 @@ namespace Xamarin.Forms.Build.Tasks
return;
if (skips.Contains(propertyName))
return;
+ if (parentNode is IElementNode && ((IElementNode)parentNode).SkipProperties.Contains (propertyName))
+ return;
var markupString = markupnode.MarkupString;
var node = ParseExpression(ref markupString, Context, markupnode.NamespaceResolver, markupnode) as IElementNode;
if (node != null)
diff --git a/Xamarin.Forms.Build.Tasks/NodeILExtensions.cs b/Xamarin.Forms.Build.Tasks/NodeILExtensions.cs
index 084151ba..a7fed895 100644
--- a/Xamarin.Forms.Build.Tasks/NodeILExtensions.cs
+++ b/Xamarin.Forms.Build.Tasks/NodeILExtensions.cs
@@ -51,6 +51,21 @@ namespace Xamarin.Forms.Build.Tasks
var module = context.Body.Method.Module;
var str = (string)node.Value;
+ //If the TypeConverter has a ProvideCompiledAttribute that can be resolved, shortcut this
+ var compiledConverterName = typeConverter?.GetCustomAttribute (module.Import(typeof(ProvideCompiledAttribute)))?.ConstructorArguments?.First().Value as string;
+ Type compiledConverterType;
+ if (compiledConverterName != null && (compiledConverterType = Type.GetType (compiledConverterName)) != null) {
+ var compiledConverter = Activator.CreateInstance (compiledConverterType);
+ var converter = typeof(ICompiledTypeConverter).GetMethods ().FirstOrDefault (md => md.Name == "ConvertFromString");
+ var instructions = (IEnumerable<Instruction>)converter.Invoke (compiledConverter, new object[] {
+ node.Value as string, context.Body.Method.Module, node as BaseNode});
+ foreach (var i in instructions)
+ yield return i;
+ if (targetTypeRef.IsValueType && boxValueTypes)
+ yield return Instruction.Create (OpCodes.Box, module.Import (targetTypeRef));
+ yield break;
+ }
+
//If there's a [TypeConverter], use it
if (typeConverter != null)
{
diff --git a/Xamarin.Forms.Build.Tasks/SetPropertiesVisitor.cs b/Xamarin.Forms.Build.Tasks/SetPropertiesVisitor.cs
index 734a34c2..f481cbee 100644
--- a/Xamarin.Forms.Build.Tasks/SetPropertiesVisitor.cs
+++ b/Xamarin.Forms.Build.Tasks/SetPropertiesVisitor.cs
@@ -60,6 +60,8 @@ namespace Xamarin.Forms.Build.Tasks
if (skips.Contains(propertyName))
return;
+ if (parentNode is IElementNode && ((IElementNode)parentNode).SkipProperties.Contains (propertyName))
+ return;
if (propertyName.NamespaceURI == "http://schemas.openxmlformats.org/markup-compatibility/2006" &&
propertyName.LocalName == "Ignorable")
return;
@@ -105,6 +107,11 @@ namespace Xamarin.Forms.Build.Tasks
if (propertyName != XmlName.Empty)
{
+ if (skips.Contains(propertyName))
+ return;
+ if (parentNode is IElementNode && ((IElementNode)parentNode).SkipProperties.Contains (propertyName))
+ return;
+
if (propertyName == XmlName._CreateContent)
SetDataTemplate((IElementNode)parentNode, node, Context, node);
else
@@ -136,6 +143,8 @@ namespace Xamarin.Forms.Build.Tasks
var name = new XmlName(node.NamespaceURI, contentProperty);
if (skips.Contains(name))
return;
+ if (parentNode is IElementNode && ((IElementNode)parentNode).SkipProperties.Contains (propertyName))
+ return;
Context.IL.Append(SetPropertyValue(Context.Variables[(IElementNode)parentNode], name, node, Context, node));
}
}
@@ -151,7 +160,8 @@ namespace Xamarin.Forms.Build.Tasks
if (skips.Contains(parentList.XmlName))
return;
-
+ if (parentNode is IElementNode && ((IElementNode)parentNode).SkipProperties.Contains (propertyName))
+ return;
var elementType = parent.VariableType;
var localname = parentList.XmlName.LocalName;
@@ -775,4 +785,4 @@ namespace Xamarin.Forms.Build.Tasks
return vardefref.VariableDefinition;
}
}
-} \ No newline at end of file
+}
diff --git a/Xamarin.Forms.Build.Tasks/Xamarin.Forms.Build.Tasks.csproj b/Xamarin.Forms.Build.Tasks/Xamarin.Forms.Build.Tasks.csproj
index d24f42fc..b7250038 100644
--- a/Xamarin.Forms.Build.Tasks/Xamarin.Forms.Build.Tasks.csproj
+++ b/Xamarin.Forms.Build.Tasks/Xamarin.Forms.Build.Tasks.csproj
@@ -91,6 +91,13 @@
<Compile Include="CompiledMarkupExtensions\StaticExtension.cs" />
<Compile Include="CompiledMarkupExtensions\ICompiledMarkupExtension.cs" />
<Compile Include="BindablePropertyReferenceExtensions.cs" />
+ <Compile Include="CompiledConverters\BindablePropertyConverter.cs" />
+ <Compile Include="CompiledConverters\BindingTypeConverter.cs" />
+ <Compile Include="CompiledConverters\BoundsTypeConverter.cs" />
+ <Compile Include="CompiledConverters\ColorTypeConverter.cs" />
+ <Compile Include="CompiledConverters\ICompiledTypeConverter.cs" />
+ <Compile Include="CompiledConverters\LayoutOptionsConverter.cs" />
+ <Compile Include="CompiledConverters\RectangleTypeConverter.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<Target Name="AfterBuild">
@@ -126,4 +133,7 @@
</PropertyGroup>
<Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
</Target>
+ <ItemGroup>
+ <Folder Include="CompiledConverters\" />
+ </ItemGroup>
</Project>
diff --git a/Xamarin.Forms.Build.Tasks/XmlTypeExtensions.cs b/Xamarin.Forms.Build.Tasks/XmlTypeExtensions.cs
index 52460418..db182847 100644
--- a/Xamarin.Forms.Build.Tasks/XmlTypeExtensions.cs
+++ b/Xamarin.Forms.Build.Tasks/XmlTypeExtensions.cs
@@ -10,6 +10,11 @@ namespace Xamarin.Forms.Build.Tasks
{
static class XmlTypeExtensions
{
+ public static TypeReference GetTypeReference (string namespaceURI, string typename, ModuleDefinition module, IXmlLineInfo xmlInfo)
+ {
+ return new XmlType (namespaceURI, typename, null).GetTypeReference (module, xmlInfo);
+ }
+
public static TypeReference GetTypeReference(this XmlType xmlType, ModuleDefinition module, IXmlLineInfo xmlInfo)
{
var namespaceURI = xmlType.NamespaceUri;
diff --git a/Xamarin.Forms.Core.UnitTests/ButtonUnitTest.cs b/Xamarin.Forms.Core.UnitTests/ButtonUnitTest.cs
index 78d97a86..d590f8c3 100644
--- a/Xamarin.Forms.Core.UnitTests/ButtonUnitTest.cs
+++ b/Xamarin.Forms.Core.UnitTests/ButtonUnitTest.cs
@@ -202,4 +202,4 @@ namespace Xamarin.Forms.Core.UnitTests
Assert.AreEqual(layout1.Spacing, bcl.Spacing);
}
}
-}
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/BindablePropertyConverter.cs b/Xamarin.Forms.Core/BindablePropertyConverter.cs
index af3fd5b6..a7398e08 100644
--- a/Xamarin.Forms.Core/BindablePropertyConverter.cs
+++ b/Xamarin.Forms.Core/BindablePropertyConverter.cs
@@ -7,6 +7,7 @@ using Xamarin.Forms.Xaml;
namespace Xamarin.Forms
{
+ [Xaml.ProvideCompiled("Xamarin.Forms.Core.XamlC.BindablePropertyConverter")]
public sealed class BindablePropertyConverter : TypeConverter, IExtendedTypeConverter
{
object IExtendedTypeConverter.ConvertFrom(CultureInfo culture, object value, IServiceProvider serviceProvider)
@@ -51,10 +52,7 @@ namespace Xamarin.Forms
type = (parentValuesProvider.TargetObject as Trigger).TargetType;
if (type == null)
- {
- string msg = string.Format("Can't resolve {0}", parts[0]);
- throw new XamlParseException(msg, lineinfo);
- }
+ throw new XamlParseException($"Can't resolve {parts [0]}", lineinfo);
return ConvertFrom(type, parts[0], lineinfo);
}
@@ -67,8 +65,7 @@ namespace Xamarin.Forms
}
return ConvertFrom(type, parts[1], lineinfo);
}
- string emsg = string.Format("Can't resolve {0}. Syntax is [[ns:]Type.]PropertyName.", value);
- throw new XamlParseException(emsg, lineinfo);
+ throw new XamlParseException($"Can't resolve {value}. Syntax is [[prefix:]Type.]PropertyName.", lineinfo);
}
public override object ConvertFromInvariantString(string value)
@@ -83,7 +80,7 @@ namespace Xamarin.Forms
string[] parts = value.Split('.');
if (parts.Length != 2)
{
- Log.Warning(null, "Can't resolve {0}. Accepted syntax is Type.PropertyName.", value);
+ Log.Warning(null, $"Can't resolve {value}. Accepted syntax is Type.PropertyName.");
return null;
}
Type type = Type.GetType("Xamarin.Forms." + parts[0]);
@@ -95,10 +92,10 @@ namespace Xamarin.Forms
string name = propertyName + "Property";
FieldInfo bpinfo = type.GetField(fi => fi.Name == name && fi.IsStatic && fi.IsPublic && fi.FieldType == typeof(BindableProperty));
if (bpinfo == null)
- throw new XamlParseException(string.Format("Can't resolve {0} on {1}", name, type.Name), lineinfo);
+ throw new XamlParseException($"Can't resolve {name} on {type.Name}", lineinfo);
var bp = bpinfo.GetValue(null) as BindableProperty;
if (bp.PropertyName != propertyName)
- throw new XamlParseException(string.Format("The PropertyName of {0}.{1} is not {2}", type.Name, name, propertyName), lineinfo);
+ throw new XamlParseException($"The PropertyName of {type.Name}.{name} is not {propertyName}", lineinfo);
return bp;
}
}
diff --git a/Xamarin.Forms.Core/BindingTypeConverter.cs b/Xamarin.Forms.Core/BindingTypeConverter.cs
index 07dda896..2e57ac88 100644
--- a/Xamarin.Forms.Core/BindingTypeConverter.cs
+++ b/Xamarin.Forms.Core/BindingTypeConverter.cs
@@ -1,5 +1,6 @@
namespace Xamarin.Forms
{
+ [Xaml.ProvideCompiled("Xamarin.Forms.Core.XamlC.BindingTypeConverter")]
public sealed class BindingTypeConverter : TypeConverter
{
public override object ConvertFromInvariantString(string value)
diff --git a/Xamarin.Forms.Core/BoundsTypeConverter.cs b/Xamarin.Forms.Core/BoundsTypeConverter.cs
index 549b7b63..68464f8e 100644
--- a/Xamarin.Forms.Core/BoundsTypeConverter.cs
+++ b/Xamarin.Forms.Core/BoundsTypeConverter.cs
@@ -3,7 +3,8 @@ using System.Globalization;
namespace Xamarin.Forms
{
- public class BoundsTypeConverter : TypeConverter
+ [Xaml.ProvideCompiled ("Xamarin.Forms.Core.XamlC.BoundsTypeConverter")]
+ public sealed class BoundsTypeConverter : TypeConverter
{
public override object ConvertFromInvariantString(string value)
{
@@ -36,7 +37,7 @@ namespace Xamarin.Forms
return new Rectangle(x, y, w, h);
}
- throw new InvalidOperationException(string.Format("Cannot convert \"{0}\" into {1}", value, typeof(Rectangle)));
+ throw new InvalidOperationException($"Cannot convert \"{value}\" into {typeof(Rectangle)}");
}
}
} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/ColorTypeConverter.cs b/Xamarin.Forms.Core/ColorTypeConverter.cs
index 612174e9..0eb6fab3 100644
--- a/Xamarin.Forms.Core/ColorTypeConverter.cs
+++ b/Xamarin.Forms.Core/ColorTypeConverter.cs
@@ -1,9 +1,9 @@
using System;
using System.Linq;
-using System.Reflection;
namespace Xamarin.Forms
{
+ [Xaml.ProvideCompiled("Xamarin.Forms.Core.XamlC.ColorTypeConverter")]
public class ColorTypeConverter : TypeConverter
{
public override object ConvertFromInvariantString(string value)
@@ -16,59 +16,39 @@ namespace Xamarin.Forms
if (parts.Length == 1 || (parts.Length == 2 && parts[0] == "Color"))
{
string color = parts[parts.Length - 1];
- switch (color)
- {
- case "Default":
- return Color.Default;
- case "Transparent":
- return Color.Transparent;
- case "Aqua":
- return Color.Aqua;
- case "Black":
- return Color.Black;
- case "Blue":
- return Color.Blue;
- case "Fuchsia":
- return Color.Fuchsia;
- case "Gray":
- return Color.Gray;
- case "Green":
- return Color.Green;
- case "Lime":
- return Color.Lime;
- case "Maroon":
- return Color.Maroon;
- case "Navy":
- return Color.Navy;
- case "Olive":
- return Color.Olive;
- case "Orange":
- return Color.Orange;
- case "Purple":
- return Color.Purple;
- case "Pink":
- return Color.Pink;
- case "Red":
- return Color.Red;
- case "Silver":
- return Color.Silver;
- case "Teal":
- return Color.Teal;
- case "White":
- return Color.White;
- case "Yellow":
- return Color.Yellow;
+ switch (color) {
+ case "Default": return Color.Default;
+ case "Accent": return Color.Accent;
+ case "Transparent": return Color.Transparent;
+ case "Aqua": return Color.Aqua;
+ case "Black": return Color.Black;
+ case "Blue": return Color.Blue;
+ case "Fuchsia": return Color.Fuchsia;
+ case "Gray": return Color.Gray;
+ case "Green": return Color.Green;
+ case "Lime": return Color.Lime;
+ case "Maroon": return Color.Maroon;
+ case "Navy": return Color.Navy;
+ case "Olive": return Color.Olive;
+ case "Orange": return Color.Orange;
+ case "Purple": return Color.Purple;
+ case "Pink": return Color.Pink;
+ case "Red": return Color.Red;
+ case "Silver": return Color.Silver;
+ case "Teal": return Color.Teal;
+ case "White": return Color.White;
+ case "Yellow": return Color.Yellow;
}
- FieldInfo field = typeof(Color).GetFields().FirstOrDefault(fi => fi.IsStatic && fi.Name == color);
+ var field = typeof(Color).GetFields().FirstOrDefault(fi => fi.IsStatic && fi.Name == color);
if (field != null)
return (Color)field.GetValue(null);
- PropertyInfo property = typeof(Color).GetProperties().FirstOrDefault(pi => pi.Name == color && pi.CanRead && pi.GetMethod.IsStatic);
+ var property = typeof(Color).GetProperties().FirstOrDefault(pi => pi.Name == color && pi.CanRead && pi.GetMethod.IsStatic);
if (property != null)
return (Color)property.GetValue(null, null);
}
}
- throw new InvalidOperationException(string.Format("Cannot convert \"{0}\" into {1}", value, typeof(Color)));
+ throw new InvalidOperationException($"Cannot convert \"{value}\" into {typeof(Color)}");
}
}
} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/LayoutOptionsConverter.cs b/Xamarin.Forms.Core/LayoutOptionsConverter.cs
index 746e56ce..6dae0f02 100644
--- a/Xamarin.Forms.Core/LayoutOptionsConverter.cs
+++ b/Xamarin.Forms.Core/LayoutOptionsConverter.cs
@@ -4,22 +4,32 @@ using System.Reflection;
namespace Xamarin.Forms
{
+ [Xaml.ProvideCompiled("Xamarin.Forms.Core.XamlC.LayoutOptionsConverter")]
public sealed class LayoutOptionsConverter : TypeConverter
{
public override object ConvertFromInvariantString(string value)
{
- if (value != null)
- {
- string[] parts = value.Split('.');
- if (parts.Length > 2 || (parts.Length == 2 && parts[0] != "LayoutOptions"))
- throw new InvalidOperationException(string.Format("Cannot convert \"{0}\" into {1}", value, typeof(LayoutOptions)));
- value = parts[parts.Length - 1];
+ if (value != null) {
+ var parts = value.Split('.');
+ if (parts.Length > 2 || (parts.Length == 2 && parts [0] != "LayoutOptions"))
+ throw new InvalidOperationException($"Cannot convert \"{value}\" into {typeof(LayoutOptions)}");
+ value = parts [parts.Length - 1];
+ switch (value) {
+ case "Start": return LayoutOptions.Start;
+ case "Center": return LayoutOptions.Center;
+ case "End": return LayoutOptions.End;
+ case "Fill": return LayoutOptions.Fill;
+ case "StartAndExpand": return LayoutOptions.StartAndExpand;
+ case "CenterAndExpand": return LayoutOptions.CenterAndExpand;
+ case "EndAndExpand": return LayoutOptions.EndAndExpand;
+ case "FillAndExpand": return LayoutOptions.FillAndExpand;
+ }
FieldInfo field = typeof(LayoutOptions).GetFields().FirstOrDefault(fi => fi.IsStatic && fi.Name == value);
if (field != null)
return (LayoutOptions)field.GetValue(null);
}
- throw new InvalidOperationException(string.Format("Cannot convert \"{0}\" into {1}", value, typeof(LayoutOptions)));
+ throw new InvalidOperationException($"Cannot convert \"{value}\" into {typeof(LayoutOptions)}");
}
}
} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/ParameterAttribute.cs b/Xamarin.Forms.Core/ParameterAttribute.cs
index d8a07267..629a454f 100644
--- a/Xamarin.Forms.Core/ParameterAttribute.cs
+++ b/Xamarin.Forms.Core/ParameterAttribute.cs
@@ -10,6 +10,6 @@ namespace Xamarin.Forms
Name = name;
}
- public string Name { get; set; }
+ public string Name { get; }
}
} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/ProvideCompiledAttribute.cs b/Xamarin.Forms.Core/ProvideCompiledAttribute.cs
new file mode 100644
index 00000000..f8e261ba
--- /dev/null
+++ b/Xamarin.Forms.Core/ProvideCompiledAttribute.cs
@@ -0,0 +1,15 @@
+using System;
+
+namespace Xamarin.Forms.Xaml
+{
+ [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
+ sealed class ProvideCompiledAttribute : Attribute
+ {
+ public string CompiledVersion { get; }
+
+ public ProvideCompiledAttribute (string compiledVersion)
+ {
+ CompiledVersion = compiledVersion;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/RectangleTypeConverter.cs b/Xamarin.Forms.Core/RectangleTypeConverter.cs
index b17a8c26..421c8060 100644
--- a/Xamarin.Forms.Core/RectangleTypeConverter.cs
+++ b/Xamarin.Forms.Core/RectangleTypeConverter.cs
@@ -3,6 +3,7 @@ using System.Globalization;
namespace Xamarin.Forms
{
+ [Xaml.ProvideCompiled("Xamarin.Forms.Core.XamlC.RectangleTypeConverter")]
public class RectangleTypeConverter : TypeConverter
{
public override object ConvertFromInvariantString(string value)
diff --git a/Xamarin.Forms.Core/TypeConverter.cs b/Xamarin.Forms.Core/TypeConverter.cs
index 57ea3b4c..52b14d4e 100644
--- a/Xamarin.Forms.Core/TypeConverter.cs
+++ b/Xamarin.Forms.Core/TypeConverter.cs
@@ -8,7 +8,7 @@ namespace Xamarin.Forms
public virtual bool CanConvertFrom(Type sourceType)
{
if (sourceType == null)
- throw new ArgumentNullException("sourceType");
+ throw new ArgumentNullException(nameof(sourceType));
return sourceType == typeof(string);
}
diff --git a/Xamarin.Forms.Core/Xamarin.Forms.Core.csproj b/Xamarin.Forms.Core/Xamarin.Forms.Core.csproj
index a07b88ca..90578e3c 100644
--- a/Xamarin.Forms.Core/Xamarin.Forms.Core.csproj
+++ b/Xamarin.Forms.Core/Xamarin.Forms.Core.csproj
@@ -435,6 +435,7 @@
<Compile Include="NativeBindingHelpers.cs" />
<Compile Include="INativeValueConverterService.cs" />
<Compile Include="INativeBindingService.cs" />
+ <Compile Include="ProvideCompiledAttribute.cs" />
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
<ItemGroup>
diff --git a/Xamarin.Forms.Xaml.UnitTests/CompiledTypeConverter.xaml b/Xamarin.Forms.Xaml.UnitTests/CompiledTypeConverter.xaml
new file mode 100644
index 00000000..56d7e082
--- /dev/null
+++ b/Xamarin.Forms.Xaml.UnitTests/CompiledTypeConverter.xaml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
+ xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
+ x:Class="Xamarin.Forms.Xaml.UnitTests.CompiledTypeConverter"
+ RectangleP="0,1,2,4"
+ RectangleBP="4,8,16,32"
+ BackgroundColor="Pink">
+ <Label HorizontalOptions="EndAndExpand" />
+</ContentPage> \ No newline at end of file
diff --git a/Xamarin.Forms.Xaml.UnitTests/CompiledTypeConverter.xaml.cs b/Xamarin.Forms.Xaml.UnitTests/CompiledTypeConverter.xaml.cs
new file mode 100644
index 00000000..092d67de
--- /dev/null
+++ b/Xamarin.Forms.Xaml.UnitTests/CompiledTypeConverter.xaml.cs
@@ -0,0 +1,45 @@
+using System;
+using System.Collections.Generic;
+
+using Xamarin.Forms;
+using NUnit.Framework;
+
+namespace Xamarin.Forms.Xaml.UnitTests
+{
+ public partial class CompiledTypeConverter : ContentPage
+ {
+ public static readonly BindableProperty RectangleBPProperty =
+ BindableProperty.Create ("RectangleBP", typeof(Rectangle), typeof(CompiledTypeConverter), default(Rectangle));
+
+ public Rectangle RectangleBP {
+ get { return (Rectangle)GetValue (RectangleBPProperty); }
+ set { SetValue (RectangleBPProperty, value); }
+ }
+
+ public Rectangle RectangleP { get; set; }
+
+ public CompiledTypeConverter ()
+ {
+ InitializeComponent ();
+ }
+
+ public CompiledTypeConverter (bool useCompiledXaml)
+ {
+ //this stub will be replaced at compile time
+ }
+
+ [TestFixture]
+ public class Tests
+ {
+ [TestCase (false)]
+ [TestCase (true)]
+ public void CompiledTypeConverterAreInvoked (bool useCompiledXaml)
+ {
+ var p = new CompiledTypeConverter (useCompiledXaml);
+ Assert.AreEqual (new Rectangle (0, 1, 2, 4), p.RectangleP);
+ Assert.AreEqual (new Rectangle (4, 8, 16, 32), p.RectangleBP);
+ Assert.AreEqual (Color.Pink, p.BackgroundColor);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Xaml.UnitTests/Validation/SetterOnNonBP.xaml.cs b/Xamarin.Forms.Xaml.UnitTests/Validation/SetterOnNonBP.xaml.cs
index f39e5dd3..cf1b0294 100644
--- a/Xamarin.Forms.Xaml.UnitTests/Validation/SetterOnNonBP.xaml.cs
+++ b/Xamarin.Forms.Xaml.UnitTests/Validation/SetterOnNonBP.xaml.cs
@@ -8,6 +8,7 @@ namespace Xamarin.Forms.Xaml.UnitTests
public string NonBindable { get; set; }
}
+ [XamlCompilation(XamlCompilationOptions.Skip)]
public partial class SetterOnNonBP : ContentPage
{
public SetterOnNonBP ()
@@ -27,7 +28,10 @@ namespace Xamarin.Forms.Xaml.UnitTests
[TestCase (true)]
public void ShouldThrow (bool useCompiledXaml)
{
- Assert.Throws (new XamlParseExceptionConstraint (10, 13), () => new SetterOnNonBP (useCompiledXaml));
+ if (!useCompiledXaml)
+ Assert.Throws(new XamlParseExceptionConstraint(10, 13), () => new SetterOnNonBP(useCompiledXaml));
+ else
+ Assert.Throws(new XamlParseExceptionConstraint(10, 13), () => MockCompiler.Compile(typeof(SetterOnNonBP)));
}
}
}
diff --git a/Xamarin.Forms.Xaml.UnitTests/Xamarin.Forms.Xaml.UnitTests.csproj b/Xamarin.Forms.Xaml.UnitTests/Xamarin.Forms.Xaml.UnitTests.csproj
index b2f5e0ac..dc7f7ea1 100644
--- a/Xamarin.Forms.Xaml.UnitTests/Xamarin.Forms.Xaml.UnitTests.csproj
+++ b/Xamarin.Forms.Xaml.UnitTests/Xamarin.Forms.Xaml.UnitTests.csproj
@@ -368,6 +368,9 @@
<Compile Include="XStaticException.xaml.cs">
<DependentUpon>XStaticException.xaml</DependentUpon>
</Compile>
+ <Compile Include="CompiledTypeConverter.xaml.cs" >
+ <DependentUpon>CompiledTypeConverter.xaml</DependentUpon>
+ </Compile>
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<Import Project="..\.nuspec\Xamarin.Forms.Debug.targets" />
@@ -653,6 +656,9 @@
<EmbeddedResource Include="XStaticException.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
+ <EmbeddedResource Include="CompiledTypeConverter.xaml" >
+ <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
+ </EmbeddedResource>
</ItemGroup>
<ItemGroup>
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
diff --git a/Xamarin.Forms.Xaml/CreateValuesVisitor.cs b/Xamarin.Forms.Xaml/CreateValuesVisitor.cs
index d6848033..04cd62cb 100644
--- a/Xamarin.Forms.Xaml/CreateValuesVisitor.cs
+++ b/Xamarin.Forms.Xaml/CreateValuesVisitor.cs
@@ -74,8 +74,7 @@ namespace Xamarin.Forms.Xaml
else if (!type.GetTypeInfo().DeclaredConstructors.Any(ci => ci.IsPublic && ci.GetParameters().Length == 0) &&
!ValidateCtorArguments(type, node, out ctorargname))
{
- throw new XamlParseException(
- String.Format("The Property {0} is required to create a {1} object.", ctorargname, type.FullName), node);
+ throw new XamlParseException($"The Property {ctorargname} is required to create a {type.FullName} object.", node);
}
else
{
diff --git a/Xamarin.Forms.Xaml/XamlNode.cs b/Xamarin.Forms.Xaml/XamlNode.cs
index f794428b..a19e7d5c 100644
--- a/Xamarin.Forms.Xaml/XamlNode.cs
+++ b/Xamarin.Forms.Xaml/XamlNode.cs
@@ -52,10 +52,9 @@ namespace Xamarin.Forms.Xaml
}
public string NamespaceUri { get; }
-
public string Name { get; }
-
public IList<XmlType> TypeArguments { get; private set; }
+
}
internal abstract class BaseNode : IXmlLineInfo, INode