Browse Source

Fix assigning binding paths via the property.

pull/2734/head
Jeremy Koritzinsky 6 years ago
parent
commit
d89f5d019b
  1. 1
      src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
  2. 4
      src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlCompiler.cs
  3. 56
      src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathParser.cs
  4. 22
      src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/XamlIlBindingPathHelper.cs
  5. 29
      tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs

1
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
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
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; }
}
}

22
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;

29
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")

Loading…
Cancel
Save