From c92297047c01112fd4a6e7695a40acb274fdf7a7 Mon Sep 17 00:00:00 2001 From: Stephane Delcroix Date: Tue, 12 Apr 2016 18:46:39 +0200 Subject: [PATCH] [X] internal CreateFromXaml () (#77) --- Xamarin.Forms.Build.Tasks/ILRootNode.cs | 2 +- .../TypeConverterTestsLegacy.cs | 12 ++--- .../Xamarin.Forms.Xaml.UnitTests.csproj | 1 + .../XamlLoaderCreateTests.cs | 38 ++++++++++++++ Xamarin.Forms.Xaml/ApplyPropertiesVisitor.cs | 23 ++++++--- Xamarin.Forms.Xaml/CreateValuesVisitor.cs | 2 +- Xamarin.Forms.Xaml/HydratationContext.cs | 2 + Xamarin.Forms.Xaml/XamlLoader.cs | 58 ++++++++++++++++------ Xamarin.Forms.Xaml/XamlNode.cs | 2 +- 9 files changed, 110 insertions(+), 30 deletions(-) create mode 100644 Xamarin.Forms.Xaml.UnitTests/XamlLoaderCreateTests.cs diff --git a/Xamarin.Forms.Build.Tasks/ILRootNode.cs b/Xamarin.Forms.Build.Tasks/ILRootNode.cs index b50d620..1c59cd3 100644 --- a/Xamarin.Forms.Build.Tasks/ILRootNode.cs +++ b/Xamarin.Forms.Build.Tasks/ILRootNode.cs @@ -5,7 +5,7 @@ namespace Xamarin.Forms.Build.Tasks { class ILRootNode : RootNode { - public ILRootNode(XmlType xmlType, TypeReference typeReference) : base(xmlType) + public ILRootNode(XmlType xmlType, TypeReference typeReference) : base(xmlType, null) { TypeReference = typeReference; } diff --git a/Xamarin.Forms.Xaml.UnitTests/TypeConverterTestsLegacy.cs b/Xamarin.Forms.Xaml.UnitTests/TypeConverterTestsLegacy.cs index 9d5051f..4066bb2 100644 --- a/Xamarin.Forms.Xaml.UnitTests/TypeConverterTestsLegacy.cs +++ b/Xamarin.Forms.Xaml.UnitTests/TypeConverterTestsLegacy.cs @@ -123,7 +123,7 @@ namespace Xamarin.Forms.Xaml.UnitTests var bindable = new Bindable (); Assert.IsNull (bindable.Baz); - var rootNode = new XamlLoader.RuntimeRootNode (new XmlType("clr-namespace:Xamarin.Forms.Xaml.UnitTests;assembly=Xamarin.Forms.Xaml.UnitTests","Bindable",null), bindable) { + var rootNode = new XamlLoader.RuntimeRootNode (new XmlType("clr-namespace:Xamarin.Forms.Xaml.UnitTests;assembly=Xamarin.Forms.Xaml.UnitTests","Bindable",null), bindable, null) { Properties = { { new XmlName (null, "Baz"), node }, } @@ -142,7 +142,7 @@ namespace Xamarin.Forms.Xaml.UnitTests var bindable = new Bindable (); Assert.IsNull (bindable.Baz); - var rootNode = new XamlLoader.RuntimeRootNode (new XmlType("clr-namespace:Xamarin.Forms.Xaml.UnitTests;assembly=Xamarin.Forms.Xaml.UnitTests","Bindable",null), bindable) { + var rootNode = new XamlLoader.RuntimeRootNode (new XmlType("clr-namespace:Xamarin.Forms.Xaml.UnitTests;assembly=Xamarin.Forms.Xaml.UnitTests","Bindable",null), bindable, null) { Properties = { { new XmlName (null, "Baz"), node }, } @@ -159,7 +159,7 @@ namespace Xamarin.Forms.Xaml.UnitTests var bindable = new Bindable (); Assert.IsNull (bindable.Foo); - var rootNode = new XamlLoader.RuntimeRootNode (new XmlType("clr-namespace:Xamarin.Forms.Xaml.UnitTests;assembly=Xamarin.Forms.Xaml.UnitTests","Bindable",null), bindable) { + var rootNode = new XamlLoader.RuntimeRootNode (new XmlType("clr-namespace:Xamarin.Forms.Xaml.UnitTests;assembly=Xamarin.Forms.Xaml.UnitTests","Bindable",null), bindable, null) { Properties = { { new XmlName (null, "Foo"), node }, } @@ -180,7 +180,7 @@ namespace Xamarin.Forms.Xaml.UnitTests var bindable = new Bindable (); Assert.IsNull (bindable.Bar); - var rootNode = new XamlLoader.RuntimeRootNode (new XmlType("clr-namespace:Xamarin.Forms.Xaml.UnitTests;assembly=Xamarin.Forms.Xaml.UnitTests","Bindable",null), bindable) { + var rootNode = new XamlLoader.RuntimeRootNode (new XmlType("clr-namespace:Xamarin.Forms.Xaml.UnitTests;assembly=Xamarin.Forms.Xaml.UnitTests","Bindable",null), bindable, null) { Properties = { { new XmlName (null, "Bar"), node }, } @@ -200,7 +200,7 @@ namespace Xamarin.Forms.Xaml.UnitTests var bindable = new Bindable (); Assert.IsNull (Bindable.GetQux (bindable)); - var rootNode = new XamlLoader.RuntimeRootNode (new XmlType("clr-namespace:Xamarin.Forms.Xaml.UnitTests;assembly=Xamarin.Forms.Xaml.UnitTests","Bindable",null), bindable) { + var rootNode = new XamlLoader.RuntimeRootNode (new XmlType("clr-namespace:Xamarin.Forms.Xaml.UnitTests;assembly=Xamarin.Forms.Xaml.UnitTests","Bindable",null), bindable, null) { Properties = { { new XmlName ("clr-namespace:Xamarin.Forms.Xaml.UnitTests;assembly=Xamarin.Forms.Xaml.UnitTests", "Bindable.Qux"), node }, } @@ -220,7 +220,7 @@ namespace Xamarin.Forms.Xaml.UnitTests var bindable = new Bindable (); Assert.IsNull (bindable.FooBar); - var rootNode = new XamlLoader.RuntimeRootNode (new XmlType("clr-namespace:Xamarin.Forms.Xaml.UnitTests;assembly=Xamarin.Forms.Xaml.UnitTests","Bindable",null), bindable) { + var rootNode = new XamlLoader.RuntimeRootNode (new XmlType("clr-namespace:Xamarin.Forms.Xaml.UnitTests;assembly=Xamarin.Forms.Xaml.UnitTests","Bindable",null), bindable, null) { Properties = { { new XmlName (null, "FooBar"), node }, } diff --git a/Xamarin.Forms.Xaml.UnitTests/Xamarin.Forms.Xaml.UnitTests.csproj b/Xamarin.Forms.Xaml.UnitTests/Xamarin.Forms.Xaml.UnitTests.csproj index ff6ebee..226e8bb 100644 --- a/Xamarin.Forms.Xaml.UnitTests/Xamarin.Forms.Xaml.UnitTests.csproj +++ b/Xamarin.Forms.Xaml.UnitTests/Xamarin.Forms.Xaml.UnitTests.csproj @@ -334,6 +334,7 @@ McIgnorable.xaml + diff --git a/Xamarin.Forms.Xaml.UnitTests/XamlLoaderCreateTests.cs b/Xamarin.Forms.Xaml.UnitTests/XamlLoaderCreateTests.cs new file mode 100644 index 0000000..a175b57 --- /dev/null +++ b/Xamarin.Forms.Xaml.UnitTests/XamlLoaderCreateTests.cs @@ -0,0 +1,38 @@ +using System; +using NUnit.Framework; + +namespace Xamarin.Forms.Xaml.UnitTests +{ + [TestFixture] + public class XamlLoaderCreateTests + { + [Test] + public void CreateFromXaml () + { + var xaml = @" + + "; + + var view = XamlLoader.Create (xaml); + Assert.That (view, Is.TypeOf ()); + Assert.AreEqual ("Foo", ((Label)((ContentView)view).Content).Text); + } + + [Test] + public void CreateFromXamlDoesntFailOnMissingEventHandler () + { + var xaml = @" + "; + + Button button = null; + Assert.DoesNotThrow (() => button = XamlLoader.Create (xaml, true) as Button); + Assert.NotNull (button); + } + } +} \ No newline at end of file diff --git a/Xamarin.Forms.Xaml/ApplyPropertiesVisitor.cs b/Xamarin.Forms.Xaml/ApplyPropertiesVisitor.cs index e013a74..8978f4f 100644 --- a/Xamarin.Forms.Xaml/ApplyPropertiesVisitor.cs +++ b/Xamarin.Forms.Xaml/ApplyPropertiesVisitor.cs @@ -307,19 +307,25 @@ namespace Xamarin.Forms.Xaml if (eventInfo != null && value is string) { var methodInfo = rootElement.GetType().GetRuntimeMethods().FirstOrDefault(mi => mi.Name == (string)value); - if (methodInfo == null) - { - throw new XamlParseException(string.Format("No method {0} found on type {1}", value, rootElement.GetType()), - lineInfo); + 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) { - throw new XamlParseException(string.Format("Method {0} does not have the correct signature", value), lineInfo); + 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; } return; @@ -416,7 +422,10 @@ namespace Xamarin.Forms.Xaml } } - throw exception; + if (context.DoNotThrowOnExceptions) + System.Diagnostics.Debug.WriteLine (exception.Message); + else + throw exception; } void SetTemplate(ElementTemplate dt, INode node) diff --git a/Xamarin.Forms.Xaml/CreateValuesVisitor.cs b/Xamarin.Forms.Xaml/CreateValuesVisitor.cs index 96af918..d54a36d 100644 --- a/Xamarin.Forms.Xaml/CreateValuesVisitor.cs +++ b/Xamarin.Forms.Xaml/CreateValuesVisitor.cs @@ -66,7 +66,7 @@ namespace Xamarin.Forms.Xaml return; XamlParseException xpe; - var type = XamlParser.GetElementType(node.XmlType, node, Context.RootElement.GetType().GetTypeInfo().Assembly, + var type = XamlParser.GetElementType(node.XmlType, node, Context.RootElement?.GetType().GetTypeInfo().Assembly, out xpe); if (xpe != null) throw xpe; diff --git a/Xamarin.Forms.Xaml/HydratationContext.cs b/Xamarin.Forms.Xaml/HydratationContext.cs index befb909..7c1356d 100644 --- a/Xamarin.Forms.Xaml/HydratationContext.cs +++ b/Xamarin.Forms.Xaml/HydratationContext.cs @@ -18,5 +18,7 @@ namespace Xamarin.Forms.Xaml public HydratationContext ParentContext { get; set; } public BindableObject RootElement { get; set; } + + public bool DoNotThrowOnExceptions { get; set; } } } \ No newline at end of file diff --git a/Xamarin.Forms.Xaml/XamlLoader.cs b/Xamarin.Forms.Xaml/XamlLoader.cs index 8bf08f6..575f2c7 100644 --- a/Xamarin.Forms.Xaml/XamlLoader.cs +++ b/Xamarin.Forms.Xaml/XamlLoader.cs @@ -62,23 +62,53 @@ namespace Xamarin.Forms.Xaml continue; } - var rootnode = new RuntimeRootNode(new XmlType(reader.NamespaceURI, reader.Name, null), view); - - XamlParser.ParseXaml(rootnode, reader); + var rootnode = new RuntimeRootNode (new XmlType (reader.NamespaceURI, reader.Name, null), view, (IXmlNamespaceResolver)reader); + XamlParser.ParseXaml (rootnode, reader); + Visit (rootnode, new HydratationContext { RootElement = view }); + break; + } + } + } - var visitorContext = new HydratationContext { RootElement = view }; + public static object Create (string xaml, bool doNotThrow = false) + { + object inflatedView = null; + using (var reader = XmlReader.Create (new StringReader (xaml))) { + while (reader.Read ()) { + //Skip until element + if (reader.NodeType == XmlNodeType.Whitespace) + continue; + if (reader.NodeType != XmlNodeType.Element) { + Debug.WriteLine ("Unhandled node {0} {1} {2}", reader.NodeType, reader.Name, reader.Value); + continue; + } - rootnode.Accept(new XamlNodeVisitor((node, parent) => node.Parent = parent), null); - //set parents for {StaticResource} - rootnode.Accept(new ExpandMarkupsVisitor(visitorContext), null); - rootnode.Accept(new NamescopingVisitor(visitorContext), null); //set namescopes for {x:Reference} - rootnode.Accept(new CreateValuesVisitor(visitorContext), null); - rootnode.Accept(new RegisterXNamesVisitor(visitorContext), null); - rootnode.Accept(new FillResourceDictionariesVisitor(visitorContext), null); - rootnode.Accept(new ApplyPropertiesVisitor(visitorContext, true), null); + var rootnode = new RuntimeRootNode (new XmlType (reader.NamespaceURI, reader.Name, null), null, (IXmlNamespaceResolver)reader); + XamlParser.ParseXaml (rootnode, reader); + var visitorContext = new HydratationContext { + DoNotThrowOnExceptions = doNotThrow, + }; + var cvv = new CreateValuesVisitor (visitorContext); + cvv.Visit ((ElementNode)rootnode, null); + inflatedView = rootnode.Root = visitorContext.Values [rootnode]; + visitorContext.RootElement = inflatedView as BindableObject; + + Visit (rootnode, visitorContext); break; } } + return inflatedView; + } + + static void Visit (RootNode rootnode, HydratationContext visitorContext) + { + rootnode.Accept (new XamlNodeVisitor ((node, parent) => node.Parent = parent), null); //set parents for {StaticResource} + rootnode.Accept (new ExpandMarkupsVisitor (visitorContext), null); + rootnode.Accept (new NamescopingVisitor (visitorContext), null); //set namescopes for {x:Reference} + rootnode.Accept (new CreateValuesVisitor (visitorContext), null); + rootnode.Accept (new RegisterXNamesVisitor (visitorContext), null); + rootnode.Accept (new FillResourceDictionariesVisitor (visitorContext), null); + rootnode.Accept (new ApplyPropertiesVisitor (visitorContext, true), null); } static string GetXamlForType(Type type) @@ -197,12 +227,12 @@ namespace Xamarin.Forms.Xaml public class RuntimeRootNode : RootNode { - public RuntimeRootNode(XmlType xmlType, object root) : base(xmlType) + public RuntimeRootNode(XmlType xmlType, object root, IXmlNamespaceResolver resolver) : base (xmlType, resolver) { Root = root; } - public object Root { get; private set; } + public object Root { get; internal set; } } } } \ No newline at end of file diff --git a/Xamarin.Forms.Xaml/XamlNode.cs b/Xamarin.Forms.Xaml/XamlNode.cs index 2ef87c1..aa8e6c8 100644 --- a/Xamarin.Forms.Xaml/XamlNode.cs +++ b/Xamarin.Forms.Xaml/XamlNode.cs @@ -178,7 +178,7 @@ namespace Xamarin.Forms.Xaml internal abstract class RootNode : ElementNode { - protected RootNode(XmlType xmlType) : base(xmlType, xmlType.NamespaceUri, null) + protected RootNode(XmlType xmlType, IXmlNamespaceResolver nsResolver) : base(xmlType, xmlType.NamespaceUri, nsResolver) { } -- 2.7.4