From d89f5d019b8ddef544451c96915db8c8d6d6813a Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Sat, 21 Sep 2019 16:19:37 -0700 Subject: [PATCH] Fix assigning binding paths via the property. --- .../Avalonia.Markup.Xaml.csproj | 1 + .../AvaloniaXamlIlCompiler.cs | 4 ++ .../AvaloniaXamlIlBindingPathParser.cs | 56 +++++++++++++++++++ .../XamlIlBindingPathHelper.cs | 22 +++----- .../CompiledBindingExtensionTests.cs | 29 +++++++++- 5 files changed, 95 insertions(+), 17 deletions(-) create mode 100644 src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathParser.cs diff --git a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj index 09e6b27967..3e4204d79d 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj +++ b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj @@ -46,6 +46,7 @@ + diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlCompiler.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlCompiler.cs index 710faa28f4..396196cfbc 100644 --- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlCompiler.cs +++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlCompiler.cs @@ -53,6 +53,10 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions // After everything else + InsertBefore( + new AvaloniaXamlIlBindingPathParser() + ); + InsertBefore( new AddNameScopeRegistration(), new AvaloniaXamlIlDataContextTypeTransformer(), diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathParser.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathParser.cs new file mode 100644 index 0000000000..9a34261813 --- /dev/null +++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathParser.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Avalonia.Markup.Parsers; +using Avalonia.Utilities; +using XamlIl.Ast; +using XamlIl.Transform; +using XamlIl.TypeSystem; + +namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers +{ + class AvaloniaXamlIlBindingPathParser : IXamlIlAstTransformer + { + public IXamlIlAstNode Transform(XamlIlAstTransformationContext context, IXamlIlAstNode node) + { + if (node is XamlIlAstObjectNode binding && binding.Type.GetClrType().Equals(context.GetAvaloniaTypes().CompiledBindingExtension)) + { + if (binding.Arguments.Count > 0 && binding.Arguments[0] is XamlIlAstTextNode bindingPathText) + { + var reader = new CharacterReader(bindingPathText.Text.AsSpan()); + var (nodes, _) = BindingExpressionGrammar.Parse(ref reader); + binding.Arguments[0] = new ParsedBindingPathNode(bindingPathText, context.GetAvaloniaTypes().CompiledBindingPath, nodes); + } + else + { + var bindingPathAssignment = binding.Children.OfType() + .FirstOrDefault(v => v.Property.GetClrProperty().Name == "Path"); + + if (bindingPathAssignment != null && bindingPathAssignment.Values[0] is XamlIlAstTextNode pathValue) + { + var reader = new CharacterReader(pathValue.Text.AsSpan()); + var (nodes, _) = BindingExpressionGrammar.Parse(ref reader); + bindingPathAssignment.Values[0] = new ParsedBindingPathNode(pathValue, context.GetAvaloniaTypes().CompiledBindingPath, nodes); + } + } + } + + return node; + } + } + + class ParsedBindingPathNode : XamlIlAstNode, IXamlIlAstValueNode + { + public ParsedBindingPathNode(IXamlIlLineInfo lineInfo, IXamlIlType compiledBindingType, IList path) + : base(lineInfo) + { + Type = new XamlIlAstClrTypeReference(lineInfo, compiledBindingType, false); + Path = path; + } + + public IXamlIlAstTypeReference Type { get; } + + public IList Path { get; } + } +} diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/XamlIlBindingPathHelper.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/XamlIlBindingPathHelper.cs index 1a4facf543..69512dcf75 100644 --- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/XamlIlBindingPathHelper.cs +++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/XamlIlBindingPathHelper.cs @@ -19,40 +19,34 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions public static IXamlIlType UpdateCompiledBindingExtension(XamlIlAstTransformationContext context, XamlIlAstObjectNode binding, IXamlIlType startType) { IXamlIlType bindingResultType = null; - if (binding.Arguments.Count > 0 && binding.Arguments[0] is XamlIlAstTextNode bindingPathText) + if (binding.Arguments.Count > 0 && binding.Arguments[0] is ParsedBindingPathNode bindingPath) { - var reader = new CharacterReader(bindingPathText.Text.AsSpan()); - var grammar = BindingExpressionGrammar.Parse(ref reader); - var transformed = TransformBindingPath( context, - bindingPathText, + bindingPath, startType, - grammar.Nodes); + bindingPath.Path); bindingResultType = transformed.BindingResultType; binding.Arguments[0] = transformed; } else { - var bindingPathAssignment = binding.Children.OfType() - .FirstOrDefault(v => v.Property.GetClrProperty().Name == "Path"); + var bindingPathAssignment = binding.Children.OfType() + .FirstOrDefault(v => v.Property.Name == "Path"); if (bindingPathAssignment is null) { return startType; } - if (bindingPathAssignment.Values[0] is XamlIlAstTextNode pathValue) + if (bindingPathAssignment.Values[0] is ParsedBindingPathNode bindingPathNode) { - var reader = new CharacterReader(pathValue.Text.AsSpan()); - var grammar = BindingExpressionGrammar.Parse(ref reader); - var transformed = TransformBindingPath( context, - pathValue, + bindingPathNode, startType, - grammar.Nodes); + bindingPathNode.Path); bindingResultType = transformed.BindingResultType; bindingPathAssignment.Values[0] = transformed; diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs index 3de6e035ba..0648ca6571 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs @@ -32,7 +32,32 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions var window = (Window)loader.Load(xaml); var textBlock = window.FindControl("textBlock"); - DelayedBinding.ApplyBindings(textBlock); + var dataContext = new TestDataContext + { + StringProperty = "foobar" + }; + + window.DataContext = dataContext; + + Assert.Equal(dataContext.StringProperty, textBlock.Text); + } + } + + [Fact] + public void ResolvesPathPassedByProperty() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + +"; + var loader = new AvaloniaXamlLoader(); + var window = (Window)loader.Load(xaml); + var textBlock = window.FindControl("textBlock"); var dataContext = new TestDataContext { @@ -61,8 +86,6 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions var window = (Window)loader.Load(xaml); var textBlock = window.FindControl("textBlock"); - DelayedBinding.ApplyBindings(textBlock); - var dataContext = new TestDataContext { TaskProperty = Task.FromResult("foobar")