Browse Source

Fix compiled binding with TemplatedParent relative source

pull/8466/head
Max Katz 4 years ago
parent
commit
2619ab1759
  1. 6
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs
  2. 25
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathParser.cs
  3. 1
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlBindingPathHelper.cs
  4. 30
      tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs

6
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs

@ -45,10 +45,10 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
new AvaloniaXamlIlReorderClassesPropertiesTransformer() new AvaloniaXamlIlReorderClassesPropertiesTransformer()
); );
InsertBefore<ContentConvertTransformer>( InsertBefore<ContentConvertTransformer>(
new AvaloniaXamlIlBindingPathParser(),
new AvaloniaXamlIlSelectorTransformer(), new AvaloniaXamlIlSelectorTransformer(),
new AvaloniaXamlIlControlTemplateTargetTypeMetadataTransformer(), new AvaloniaXamlIlControlTemplateTargetTypeMetadataTransformer(),
new AvaloniaXamlIlBindingPathParser(),
new AvaloniaXamlIlPropertyPathTransformer(), new AvaloniaXamlIlPropertyPathTransformer(),
new AvaloniaXamlIlSetterTransformer(), new AvaloniaXamlIlSetterTransformer(),
new AvaloniaXamlIlConstructorServiceProviderTransformer(), new AvaloniaXamlIlConstructorServiceProviderTransformer(),

25
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathParser.cs

@ -121,11 +121,14 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
{ {
throw new XamlParseException("Only one of ElementName, Source, or RelativeSource specified as a binding source. Only one property is allowed.", binding); throw new XamlParseException("Only one of ElementName, Source, or RelativeSource specified as a binding source. Only one property is allowed.", binding);
} }
var mode = relativeSourceObject.Children var modeProperty = relativeSourceObject.Children
.OfType<XamlAstXamlPropertyValueNode>() .OfType<XamlAstXamlPropertyValueNode>()
.FirstOrDefault(x => x.Property.GetClrProperty().Name == "Mode") .FirstOrDefault(x => x.Property.GetClrProperty().Name == "Mode")?
?.Values[0] is XamlAstTextNode modeAssignedValue ? modeAssignedValue.Text : null; .Values.FirstOrDefault() as XamlAstTextNode
?? relativeSourceObject.Arguments.OfType<XamlAstTextNode>().FirstOrDefault();
var mode = modeProperty?.Text;
if (relativeSourceObject.Arguments.Count == 0 && mode == null) if (relativeSourceObject.Arguments.Count == 0 && mode == null)
{ {
mode = "FindAncestor"; mode = "FindAncestor";
@ -212,16 +215,20 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
} }
else if (mode == "TemplatedParent") else if (mode == "TemplatedParent")
{ {
var parentType = context.ParentNodes().OfType<AvaloniaXamlIlTargetTypeMetadataNode>() var contentTemplateNode = context.ParentNodes().OfType<AvaloniaXamlIlTargetTypeMetadataNode>()
.FirstOrDefault(x => .FirstOrDefault(x =>
x.ScopeType == AvaloniaXamlIlTargetTypeMetadataNode.ScopeTypes.ControlTemplate) x.ScopeType == AvaloniaXamlIlTargetTypeMetadataNode.ScopeTypes.ControlTemplate);
?.TargetType.GetClrType(); if (contentTemplateNode is null)
if (parentType is null)
{ {
throw new XamlParseException("A binding with a TemplatedParent RelativeSource has to be in a ControlTemplate.", binding); throw new XamlParseException("A binding with a TemplatedParent RelativeSource has to be in a ControlTemplate.", binding);
} }
var parentType = contentTemplateNode.TargetType.GetClrType();
if (parentType is null)
{
throw new XamlParseException("TargetType has to be set on ControlTemplate.", binding);
}
convertedNode = new TemplatedParentBindingExpressionNode { Type = parentType }; convertedNode = new TemplatedParentBindingExpressionNode { Type = parentType };
} }
else else

1
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlBindingPathHelper.cs

@ -244,6 +244,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
case TemplatedParentBindingExpressionNode templatedParent: case TemplatedParentBindingExpressionNode templatedParent:
var templatedParentField = context.GetAvaloniaTypes().StyledElement.GetAllFields() var templatedParentField = context.GetAvaloniaTypes().StyledElement.GetAllFields()
.FirstOrDefault(f => f.IsStatic && f.IsPublic && f.Name == "TemplatedParentProperty"); .FirstOrDefault(f => f.IsStatic && f.IsPublic && f.Name == "TemplatedParentProperty");
nodes.Add(new SelfPathElementNode(selfType));
nodes.Add(new XamlIlAvaloniaPropertyPropertyPathElementNode( nodes.Add(new XamlIlAvaloniaPropertyPropertyPathElementNode(
templatedParentField, templatedParentField,
templatedParent.Type)); templatedParent.Type));

30
tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs

@ -3,8 +3,8 @@ using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.ComponentModel; using System.ComponentModel;
using System.Globalization; using System.Globalization;
using System.Linq;
using System.Reactive.Subjects; using System.Reactive.Subjects;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.Presenters; using Avalonia.Controls.Presenters;
@ -17,8 +17,6 @@ using Avalonia.Markup.Xaml.Templates;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Metadata; using Avalonia.Metadata;
using Avalonia.UnitTests; using Avalonia.UnitTests;
using JetBrains.Annotations;
using XamlX;
using Xunit; using Xunit;
namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
@ -676,6 +674,32 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
} }
} }
[Fact]
public void ResolvesRelativeSourceBindingFromTemplate()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var xaml = @"
<ContentControl xmlns='https://github.com/avaloniaui'
Content='Hello'>
<ContentControl.Styles>
<Style Selector='ContentControl'>
<Setter Property='Template'>
<ControlTemplate>
<ContentPresenter Content='{CompiledBinding Content, RelativeSource={RelativeSource TemplatedParent}}' />
</ControlTemplate>
</Setter>
</Style>
</ContentControl.Styles>
</ContentControl>";
var contentControl = AvaloniaRuntimeXamlLoader.Parse<ContentControl>(xaml);
contentControl.Measure(new Size(10, 10));
var result = contentControl.GetTemplateChildren().OfType<ContentPresenter>().First();
Assert.Equal("Hello", result.Content);
}
}
[Fact] [Fact]
public void ResolvesElementNameInTemplate() public void ResolvesElementNameInTemplate()

Loading…
Cancel
Save