瀏覽代碼

Fix assigning binding paths via the property.

Jeremy Koritzinsky 6 年之前
父節點
當前提交
d89f5d019b

+ 1 - 0
src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj

@@ -46,6 +46,7 @@
         <Compile Include="XamlIl\CompilerExtensions\AvaloniaXamlIlCompilerConfiguration.cs" />
         <Compile Include="XamlIl\CompilerExtensions\Transformers\AvaloniaBindingExtensionHackTransformer.cs" />
         <Compile Include="XamlIl\CompilerExtensions\Transformers\AvaloniaXamlIlAvaloniaPropertyResolver.cs" />
+        <Compile Include="XamlIl\CompilerExtensions\Transformers\AvaloniaXamlIlBindingPathParser.cs" />
         <Compile Include="XamlIl\CompilerExtensions\Transformers\AvaloniaXamlIlBindingPathTransformer.cs" />
         <Compile Include="XamlIl\CompilerExtensions\Transformers\AvaloniaXamlIlConstructorServiceProviderTransformer.cs" />
         <Compile Include="XamlIl\CompilerExtensions\Transformers\AvaloniaXamlIlControlTemplateTargetTypeMetadataTransformer.cs" />

+ 4 - 0
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlCompiler.cs

@@ -53,6 +53,10 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
 
             // After everything else
 
+            InsertBefore<XamlIlConvertPropertyValuesToAssignmentsTransformer>(
+                new AvaloniaXamlIlBindingPathParser()
+            );
+
             InsertBefore<XamlIlNewObjectTransformer>(
                 new AddNameScopeRegistration(),
                 new AvaloniaXamlIlDataContextTypeTransformer(),

+ 56 - 0
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<XamlIlAstXamlPropertyValueNode>()
+                        .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<BindingExpressionGrammar.INode> path)
+            : base(lineInfo)
+        {
+            Type = new XamlIlAstClrTypeReference(lineInfo, compiledBindingType, false);
+            Path = path;
+        }
+
+        public IXamlIlAstTypeReference Type { get; }
+
+        public IList<BindingExpressionGrammar.INode> Path { get; }
+    }
+}

+ 8 - 14
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<XamlIlAstXamlPropertyValueNode>()
-                    .FirstOrDefault(v => v.Property.GetClrProperty().Name == "Path");
+                var bindingPathAssignment = binding.Children.OfType<XamlIlPropertyAssignmentNode>()
+                    .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;

+ 26 - 3
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>("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 = @"
+<Window xmlns='https://github.com/avaloniaui'
+        xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
+        xmlns:local='clr-namespace:Avalonia.Markup.Xaml.UnitTests.MarkupExtensions;assembly=Avalonia.Markup.Xaml.UnitTests'
+        x:DataType='local:TestDataContext'>
+    <TextBlock Text='{CompiledBinding Path=StringProperty}' Name='textBlock' />
+</Window>";
+                var loader = new AvaloniaXamlLoader();
+                var window = (Window)loader.Load(xaml);
+                var textBlock = window.FindControl<TextBlock>("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>("textBlock");
 
-                DelayedBinding.ApplyBindings(textBlock);
-
                 var dataContext = new TestDataContext
                 {
                     TaskProperty = Task.FromResult("foobar")