From 551380e71d817a4efb9bd7855c0bb603c3eab86d Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 13 Jul 2021 16:43:14 +0200 Subject: [PATCH 1/3] Added failing test for #4392. --- .../CompiledBindingExtensionTests.cs | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs index 6f549a4ffa..8cd5cf7c13 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs @@ -938,6 +938,58 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions } } + [Fact] + public void SupportsEmptyPath() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + +"; + var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); + var textBlock = window.FindControl("textBlock"); + + var dataContext = new TestDataContext + { + StringProperty = "foobar" + }; + + window.DataContext = dataContext; + + Assert.Equal(typeof(TestDataContext).FullName, textBlock.Text); + } + } + + [Fact] + public void SupportsDotPath() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + +"; + var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); + var textBlock = window.FindControl("textBlock"); + + var dataContext = new TestDataContext + { + StringProperty = "foobar" + }; + + window.DataContext = dataContext; + + Assert.Equal(typeof(TestDataContext).FullName, textBlock.Text); + } + } + void Throws(string type, Action cb) { try From 25d2ac986bf330d5dae1999e6c6cbc94ef19384e Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 13 Jul 2021 16:44:19 +0200 Subject: [PATCH 2/3] Fix "." (empty) paths with compiled bindings. Fixes #4392 --- .../Transformers/AvaloniaXamlIlBindingPathParser.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathParser.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathParser.cs index 24cded1d22..93aab596c6 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathParser.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathParser.cs @@ -9,7 +9,6 @@ using XamlX.Ast; using XamlX.Transform; using XamlX.Transform.Transformers; using XamlX.TypeSystem; - using XamlParseException = XamlX.XamlParseException; namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers @@ -21,6 +20,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers if (node is XamlAstObjectNode binding && binding.Type.GetClrType().Equals(context.GetAvaloniaTypes().CompiledBindingExtension)) { var convertedNode = ConvertLongFormPropertiesToBindingExpressionNode(context, binding); + var isEmptyExpression = false; if (binding.Arguments.Count > 0 && binding.Arguments[0] is XamlAstTextNode bindingPathText) { @@ -32,9 +32,18 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers nodes.Insert(nodes.TakeWhile(x => x is BindingExpressionGrammar.ITransformNode).Count(), convertedNode); } - binding.Arguments[0] = new ParsedBindingPathNode(bindingPathText, context.GetAvaloniaTypes().CompiledBindingPath, nodes); + if (nodes.Count == 1 && nodes[0] is BindingExpressionGrammar.EmptyExpressionNode) + { + isEmptyExpression = true; + binding.Arguments.Clear(); + } + else + binding.Arguments[0] = new ParsedBindingPathNode(bindingPathText, context.GetAvaloniaTypes().CompiledBindingPath, nodes); } else + isEmptyExpression = true; + + if (isEmptyExpression) { var bindingPathAssignment = binding.Children.OfType() .FirstOrDefault(v => v.Property.GetClrProperty().Name == "Path"); From 911262bf3f25b3be61b7af7440d13f81aac1bda4 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 13 Jul 2021 17:12:43 +0200 Subject: [PATCH 3/3] Correctly handle long-form bindings with empty path. --- .../AvaloniaXamlIlBindingPathParser.cs | 25 +++++---- .../CompiledBindingExtensionTests.cs | 52 +++++++++++++++++++ 2 files changed, 68 insertions(+), 9 deletions(-) diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathParser.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathParser.cs index 93aab596c6..890cbb69bf 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathParser.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathParser.cs @@ -20,7 +20,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers if (node is XamlAstObjectNode binding && binding.Type.GetClrType().Equals(context.GetAvaloniaTypes().CompiledBindingExtension)) { var convertedNode = ConvertLongFormPropertiesToBindingExpressionNode(context, binding); - var isEmptyExpression = false; + var foundPath = false; if (binding.Arguments.Count > 0 && binding.Arguments[0] is XamlAstTextNode bindingPathText) { @@ -34,16 +34,16 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers if (nodes.Count == 1 && nodes[0] is BindingExpressionGrammar.EmptyExpressionNode) { - isEmptyExpression = true; - binding.Arguments.Clear(); + binding.Arguments.RemoveAt(0); } else + { binding.Arguments[0] = new ParsedBindingPathNode(bindingPathText, context.GetAvaloniaTypes().CompiledBindingPath, nodes); + foundPath = true; + } } - else - isEmptyExpression = true; - if (isEmptyExpression) + if (!foundPath) { var bindingPathAssignment = binding.Children.OfType() .FirstOrDefault(v => v.Property.GetClrProperty().Name == "Path"); @@ -53,12 +53,19 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers var reader = new CharacterReader(pathValue.Text.AsSpan()); var (nodes, _) = BindingExpressionGrammar.Parse(ref reader); - if (convertedNode != null) + if (nodes.Count == 1 && nodes[0] is BindingExpressionGrammar.EmptyExpressionNode) { - nodes.Insert(nodes.TakeWhile(x => x is BindingExpressionGrammar.ITransformNode).Count(), convertedNode); + bindingPathAssignment.Values.RemoveAt(0); } + else + { + if (convertedNode != null) + { + nodes.Insert(nodes.TakeWhile(x => x is BindingExpressionGrammar.ITransformNode).Count(), convertedNode); + } - bindingPathAssignment.Values[0] = new ParsedBindingPathNode(pathValue, context.GetAvaloniaTypes().CompiledBindingPath, nodes); + bindingPathAssignment.Values[0] = new ParsedBindingPathNode(pathValue, context.GetAvaloniaTypes().CompiledBindingPath, nodes); + } } } } diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs index 8cd5cf7c13..8a61458030 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs @@ -964,6 +964,32 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions } } + [Fact] + public void SupportsEmptyPathWithStringFormat() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + +"; + var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); + var textBlock = window.FindControl("textBlock"); + + var dataContext = new TestDataContext + { + StringProperty = "foobar" + }; + + window.DataContext = dataContext; + + Assert.Equal("bar-" + typeof(TestDataContext).FullName, textBlock.Text); + } + } + [Fact] public void SupportsDotPath() { @@ -990,6 +1016,32 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions } } + [Fact] + public void SupportsExplicitDotPathWithStringFormat() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + +"; + var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); + var textBlock = window.FindControl("textBlock"); + + var dataContext = new TestDataContext + { + StringProperty = "foobar" + }; + + window.DataContext = dataContext; + + Assert.Equal("bar-" + typeof(TestDataContext).FullName, textBlock.Text); + } + } + void Throws(string type, Action cb) { try