Browse Source

Enable transforming old-style RelativeSource/ElementName properties in CompiledBindings to element path nodes during XamlIl compilation.

pull/2734/head
Jeremy Koritzinsky 7 years ago
parent
commit
c0c1ccb544
  1. 2
      src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
  2. 20
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CompiledBindingPath.cs
  3. 52
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/FindVisualAncestorNode.cs
  4. 11
      src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlCompiler.cs
  5. 228
      src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathParser.cs
  6. 52
      src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlTransformSyntheticCompiledBindingMembers.cs
  7. 3
      src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs
  8. 59
      src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/XamlIlBindingPathHelper.cs
  9. 4
      src/Markup/Avalonia.Markup/Markup/Parsers/BindingExpressionGrammar.cs
  10. 55
      tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs

2
src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj

@ -18,6 +18,7 @@
<Compile Include="MarkupExtensions\CompiledBindingExtension.cs" />
<Compile Include="MarkupExtensions\CompiledBindings\ArrayElementPlugin.cs" />
<Compile Include="MarkupExtensions\CompiledBindings\CompiledBindingPath.cs" />
<Compile Include="MarkupExtensions\CompiledBindings\FindVisualAncestorNode.cs" />
<Compile Include="MarkupExtensions\CompiledBindings\ObservableStreamPlugin.cs" />
<Compile Include="MarkupExtensions\CompiledBindings\PropertyInfoAccessorFactory.cs" />
<Compile Include="MarkupExtensions\CompiledBindings\PropertyInfoAccessorPlugin.cs" />
@ -57,6 +58,7 @@
<Compile Include="XamlIl\CompilerExtensions\Transformers\AvaloniaXamlIlPropertyPathTransformer.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\AvaloniaXamlIlRootObjectScopeTransformer.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\AvaloniaXamlIlTransformInstanceAttachedProperties.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\AvaloniaXamlIlTransformSyntheticCompiledBindingMembers.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\AvaloniaXamlIlTransitionsTypeMetadataTransformer.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\AvaloniaXamlIlWellKnownTypes.cs" />
<Compile Include="XamlIl\CompilerExtensions\XamlIlAvaloniaPropertyHelper.cs" />

20
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CompiledBindingPath.cs

@ -37,6 +37,9 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
case ArrayElementPathElement arr:
node = new PropertyAccessorNode(CommonPropertyNames.IndexerName, enableValidation, new ArrayElementPlugin(arr.Indices, arr.ElementType));
break;
case VisualAncestorPathElement visualAncestor:
node = new FindVisualAncestorNode(visualAncestor.AncestorType, visualAncestor.Level);
break;
case AncestorPathElement ancestor:
node = new FindAncestorNode(ancestor.AncestorType, ancestor.Level);
break;
@ -101,6 +104,11 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
_elements.Add(new AncestorPathElement(ancestorType, level));
return this;
}
public CompiledBindingPathBuilder VisualAncestor(Type ancestorType, int level)
{
_elements.Add(new VisualAncestorPathElement(ancestorType, level));
return this;
}
public CompiledBindingPathBuilder ElementName(INameScope nameScope, string name)
{
@ -177,6 +185,18 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
public int Level { get; }
}
internal class VisualAncestorPathElement : ICompiledBindingPathElement, IControlSourceBindingPathElement
{
public VisualAncestorPathElement(Type ancestorType, int level)
{
AncestorType = ancestorType;
Level = level;
}
public Type AncestorType { get; }
public int Level { get; }
}
internal class ElementNameElement : ICompiledBindingPathElement, IControlSourceBindingPathElement
{
public ElementNameElement(INameScope nameScope, string name)

52
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/FindVisualAncestorNode.cs

@ -0,0 +1,52 @@
using System;
using Avalonia.Data.Core;
using Avalonia.VisualTree;
namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
{
class FindVisualAncestorNode : ExpressionNode
{
private readonly int _level;
private readonly Type _ancestorType;
private IDisposable _subscription;
public FindVisualAncestorNode(Type ancestorType, int level)
{
_level = level;
_ancestorType = ancestorType;
}
public override string Description
{
get
{
if (_ancestorType == null)
{
return $"$visualparent[{_level}]";
}
else
{
return $"$visualparent[{_ancestorType.Name}, {_level}]";
}
}
}
protected override void StartListeningCore(WeakReference<object> reference)
{
if (reference.TryGetTarget(out object target) && target is IVisual visual)
{
_subscription = VisualLocator.Track(visual, _level, _ancestorType).Subscribe(ValueChanged);
}
else
{
_subscription = null;
}
}
protected override void StopListeningCore()
{
_subscription?.Dispose();
_subscription = null;
}
}
}

11
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlCompiler.cs

@ -37,26 +37,25 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
// Targeted
InsertBefore<XamlIlPropertyReferenceResolver>(new AvaloniaXamlIlTransformInstanceAttachedProperties());
InsertBefore<XamlIlPropertyReferenceResolver>(
new AvaloniaXamlIlTransformInstanceAttachedProperties(),
new AvaloniaXamlIlTransformSyntheticCompiledBindingMembers());
InsertAfter<XamlIlPropertyReferenceResolver>(new AvaloniaXamlIlAvaloniaPropertyResolver());
InsertBefore<XamlIlContentConvertTransformer>(
new AvaloniaXamlIlBindingPathParser(),
new AvaloniaXamlIlSelectorTransformer(),
new AvaloniaXamlIlControlTemplateTargetTypeMetadataTransformer(),
new AvaloniaXamlIlPropertyPathTransformer(),
new AvaloniaXamlIlSetterTransformer(),
new AvaloniaXamlIlControlTemplateTargetTypeMetadataTransformer(),
new AvaloniaXamlIlConstructorServiceProviderTransformer(),
new AvaloniaXamlIlTransitionsTypeMetadataTransformer()
);
// After everything else
InsertBefore<XamlIlConvertPropertyValuesToAssignmentsTransformer>(
new AvaloniaXamlIlBindingPathParser()
);
InsertBefore<XamlIlNewObjectTransformer>(
new AddNameScopeRegistration(),
new AvaloniaXamlIlDataContextTypeTransformer(),

228
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathParser.cs

@ -4,8 +4,10 @@ using System.Linq;
using System.Text;
using Avalonia.Markup.Parsers;
using Avalonia.Utilities;
using XamlIl;
using XamlIl.Ast;
using XamlIl.Transform;
using XamlIl.Transform.Transformers;
using XamlIl.TypeSystem;
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
@ -16,10 +18,18 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
{
if (node is XamlIlAstObjectNode binding && binding.Type.GetClrType().Equals(context.GetAvaloniaTypes().CompiledBindingExtension))
{
var convertedNode = ConvertLongFormPropertiesToBindingExpressionNode(context, binding);
if (binding.Arguments.Count > 0 && binding.Arguments[0] is XamlIlAstTextNode bindingPathText)
{
var reader = new CharacterReader(bindingPathText.Text.AsSpan());
var (nodes, _) = BindingExpressionGrammar.Parse(ref reader);
if (convertedNode != null)
{
nodes.Insert(nodes.TakeWhile(x => x is BindingExpressionGrammar.ITransformNode).Count(), convertedNode);
}
binding.Arguments[0] = new ParsedBindingPathNode(bindingPathText, context.GetAvaloniaTypes().CompiledBindingPath, nodes);
}
else
@ -31,6 +41,12 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
{
var reader = new CharacterReader(pathValue.Text.AsSpan());
var (nodes, _) = BindingExpressionGrammar.Parse(ref reader);
if (convertedNode != null)
{
nodes.Insert(nodes.TakeWhile(x => x is BindingExpressionGrammar.ITransformNode).Count(), convertedNode);
}
bindingPathAssignment.Values[0] = new ParsedBindingPathNode(pathValue, context.GetAvaloniaTypes().CompiledBindingPath, nodes);
}
}
@ -38,6 +54,201 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
return node;
}
private static BindingExpressionGrammar.INode ConvertLongFormPropertiesToBindingExpressionNode(
XamlIlAstTransformationContext context,
XamlIlAstObjectNode binding)
{
BindingExpressionGrammar.INode convertedNode = null;
var syntheticCompiledBindingProperties = binding.Children.OfType<XamlIlAstXamlPropertyValueNode>()
.Where(v => v.Property is AvaloniaSyntheticCompiledBindingProperty)
.ToList();
var elementNameProperty = syntheticCompiledBindingProperties
.FirstOrDefault(v =>
v.Property is AvaloniaSyntheticCompiledBindingProperty prop
&& prop.Name == SyntheticCompiledBindingPropertyName.ElementName);
var relativeSourceProperty = syntheticCompiledBindingProperties
.FirstOrDefault(v =>
v.Property is AvaloniaSyntheticCompiledBindingProperty prop
&& prop.Name == SyntheticCompiledBindingPropertyName.RelativeSource);
if (elementNameProperty?.Values[0] is XamlIlAstTextNode elementName)
{
convertedNode = new BindingExpressionGrammar.NameNode { Name = elementName.Text };
}
else if (elementNameProperty != null)
{
throw new XamlIlParseException($"Invalid ElementName '{elementNameProperty.Values[0]}'.", elementNameProperty.Values[0]);
}
if (GetRelativeSourceObjectFromAssignment(
context,
relativeSourceProperty,
out var relativeSourceObject))
{
if (convertedNode != null)
{
throw new XamlIlParseException("Both ElementName and RelativeSource specified as a binding source. Only one property is allowed.", binding);
}
var mode = relativeSourceObject.Children
.OfType<XamlIlAstXamlPropertyValueNode>()
.FirstOrDefault(x => x.Property.GetClrProperty().Name == "Mode")
?.Values[0] is XamlIlAstTextNode modeAssignedValue ? modeAssignedValue.Text : null;
if (relativeSourceObject.Arguments.Count == 0 && mode == null)
{
mode = "FindAncestor";
}
if (mode == "FindAncestor")
{
var ancestorLevel = relativeSourceObject.Children
.OfType<XamlIlAstXamlPropertyValueNode>()
.FirstOrDefault(x => x.Property.GetClrProperty().Name == "FindAncestor")
?.Values[0] is XamlIlAstTextNode ancestorLevelText ? int.Parse(ancestorLevelText.Text) - 1 : 0;
var treeType = relativeSourceObject.Children
.OfType<XamlIlAstXamlPropertyValueNode>()
.FirstOrDefault(x => x.Property.GetClrProperty().Name == "Tree")
?.Values[0] is XamlIlAstTextNode treeTypeValue ? treeTypeValue.Text : "Visual";
var ancestorTypeName = relativeSourceObject.Children
.OfType<XamlIlAstXamlPropertyValueNode>()
.FirstOrDefault(x => x.Property.GetClrProperty().Name == "AncestorType")
?.Values[0] as XamlIlAstTextNode;
IXamlIlType ancestorType = null;
if (ancestorTypeName is null)
{
if (treeType == "Visual")
{
throw new XamlIlParseException("AncestorType must be set for RelativeSourceMode.FindAncestor when searching the visual tree.", relativeSourceObject);
}
else if (treeType == "Logical")
{
var styledElementType = context.GetAvaloniaTypes().StyledElement;
ancestorType = context
.ParentNodes()
.OfType<XamlIlAstObjectNode>()
.Where(x => styledElementType.IsAssignableFrom(x.Type.GetClrType()))
.ElementAtOrDefault(ancestorLevel)
?.Type.GetClrType();
if (ancestorType is null)
{
throw new XamlIlParseException("Unable to resolve implicit ancestor type based on XAML tree.", relativeSourceObject);
}
}
}
else
{
ancestorType = XamlIlTypeReferenceResolver.ResolveType(
context,
ancestorTypeName.Text,
false,
ancestorTypeName,
true).GetClrType();
}
if (treeType == "Visual")
{
convertedNode = new VisualAncestorBindingExpressionNode
{
Type = ancestorType,
Level = ancestorLevel
};
}
else if (treeType == "Logical")
{
convertedNode = new LogicalAncestorBindingExpressionNode
{
Type = ancestorType,
Level = ancestorLevel
};
}
else
{
throw new XamlIlParseException($"Unknown tree type '{treeType}'.", binding);
}
}
else if (mode == "DataContext")
{
convertedNode = null;
}
else if (mode == "Self")
{
convertedNode = new BindingExpressionGrammar.SelfNode();
}
else if (mode == "TemplatedParent")
{
var parentType = context.ParentNodes().OfType<AvaloniaXamlIlTargetTypeMetadataNode>()
.FirstOrDefault(x =>
x.ScopeType == AvaloniaXamlIlTargetTypeMetadataNode.ScopeTypes.ControlTemplate)
?.TargetType.GetClrType();
if (parentType is null)
{
throw new XamlIlParseException("A binding with a TemplatedParent RelativeSource has to be in a ControlTemplate.", binding);
}
convertedNode = new TemplatedParentBindingExpressionNode { Type = parentType };
}
else
{
throw new XamlIlParseException($"Unknown RelativeSource mode '{mode}'.", binding);
}
}
if (elementNameProperty != null)
{
binding.Children.Remove(elementNameProperty);
}
if (relativeSourceProperty != null)
{
binding.Children.Remove(relativeSourceProperty);
}
return convertedNode;
}
private static bool GetRelativeSourceObjectFromAssignment(
XamlIlAstTransformationContext context,
XamlIlAstXamlPropertyValueNode relativeSourceProperty,
out XamlIlAstObjectNode relativeSourceObject)
{
relativeSourceObject = null;
if (relativeSourceProperty is null)
{
return false;
}
if (relativeSourceProperty.Values[0] is XamlIlMarkupExtensionNode me)
{
if (me.Type.GetClrType() != context.GetAvaloniaTypes().RelativeSource)
{
throw new XamlIlParseException($"Expected an object of type 'Avalonia.Data.RelativeSource'. Found a object of type '{me.Type.GetClrType().GetFqn()}'", me);
}
relativeSourceObject = (XamlIlAstObjectNode)me.Value;
return true;
}
if (relativeSourceProperty.Values[0] is XamlIlAstObjectNode on)
{
if (on.Type.GetClrType() != context.GetAvaloniaTypes().RelativeSource)
{
throw new XamlIlParseException($"Expected an object of type 'Avalonia.Data.RelativeSource'. Found a object of type '{on.Type.GetClrType().GetFqn()}'", on);
}
relativeSourceObject = on;
return true;
}
return false;
}
}
class ParsedBindingPathNode : XamlIlAstNode, IXamlIlAstValueNode
@ -53,4 +264,21 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
public IList<BindingExpressionGrammar.INode> Path { get; }
}
class VisualAncestorBindingExpressionNode : BindingExpressionGrammar.INode
{
public IXamlIlType Type { get; set; }
public int Level { get; set; }
}
class LogicalAncestorBindingExpressionNode : BindingExpressionGrammar.INode
{
public IXamlIlType Type { get; set; }
public int Level { get; set; }
}
class TemplatedParentBindingExpressionNode : BindingExpressionGrammar.INode
{
public IXamlIlType Type { get; set; }
}
}

52
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlTransformSyntheticCompiledBindingMembers.cs

@ -0,0 +1,52 @@
using System;
using System.Collections.Generic;
using System.Text;
using XamlIl;
using XamlIl.Ast;
using XamlIl.Transform;
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
{
class AvaloniaXamlIlTransformSyntheticCompiledBindingMembers : IXamlIlAstTransformer
{
public IXamlIlAstNode Transform(XamlIlAstTransformationContext context, IXamlIlAstNode node)
{
if (node is XamlIlAstNamePropertyReference prop
&& prop.TargetType is XamlIlAstClrTypeReference targetRef
&& targetRef.GetClrType().Equals(context.GetAvaloniaTypes().CompiledBindingExtension))
{
if (prop.Name == "ElementName")
{
return new AvaloniaSyntheticCompiledBindingProperty(node,
SyntheticCompiledBindingPropertyName.ElementName);
}
else if (prop.Name == "RelativeSource")
{
return new AvaloniaSyntheticCompiledBindingProperty(node,
SyntheticCompiledBindingPropertyName.RelativeSource);
}
}
return node;
}
}
enum SyntheticCompiledBindingPropertyName
{
ElementName,
RelativeSource
}
class AvaloniaSyntheticCompiledBindingProperty : XamlIlAstNode, IXamlIlAstPropertyReference
{
public SyntheticCompiledBindingPropertyName Name { get; }
public AvaloniaSyntheticCompiledBindingProperty(
IXamlIlLineInfo lineInfo,
SyntheticCompiledBindingPropertyName name)
: base(lineInfo)
{
Name = name;
}
}
}

3
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs

@ -39,6 +39,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
public IXamlIlType IItemsPresenterHost { get; }
public IXamlIlType BindingExtension { get; }
public IXamlIlType RelativeSource { get; }
public AvaloniaXamlIlWellKnownTypes(XamlIlTransformerConfiguration cfg)
{
XamlIlTypes = cfg.WellKnownTypes;
@ -91,6 +93,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
DataTemplate = cfg.TypeSystem.GetType("Avalonia.Markup.Xaml.Templates.DataTemplate");
IItemsPresenterHost = cfg.TypeSystem.GetType("Avalonia.Controls.Presenters.IItemsPresenterHost");
BindingExtension = cfg.TypeSystem.GetType("Avalonia.Markup.Xaml.MarkupExtensions.BindingExtension");
RelativeSource = cfg.TypeSystem.GetType("Avalonia.Data.RelativeSource");
}
}

59
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/XamlIlBindingPathHelper.cs

@ -118,6 +118,11 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
else
{
var clrProperty = targetType.GetAllProperties().FirstOrDefault(p => p.Name == propName.PropertyName);
if (clrProperty is null)
{
throw new XamlIlParseException($"Unable to resolve property of name '{propName.PropertyName}' on type '{targetType}'.", lineInfo);
}
nodes.Add(new XamlIlClrPropertyPathElementNode(clrProperty));
}
break;
@ -176,8 +181,38 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
case BindingExpressionGrammar.SelfNode _:
nodes.Add(new SelfPathElementNode(targetType));
break;
case VisualAncestorBindingExpressionNode visualAncestor:
nodes.Add(new FindVisualAncestorPathElementNode(visualAncestor.Type, visualAncestor.Level));
break;
case TemplatedParentBindingExpressionNode templatedParent:
var templatedParentField = context.GetAvaloniaTypes().StyledElement.GetAllFields()
.FirstOrDefault(f => f.IsStatic && f.IsPublic && f.Name == "TemplatedParentProperty");
nodes.Add(new XamlIlAvaloniaPropertyPropertyPathElementNode(
templatedParentField,
templatedParent.Type));
break;
case BindingExpressionGrammar.AncestorNode ancestor:
nodes.Add(new FindAncestorPathElementNode(GetType(ancestor.Namespace, ancestor.TypeName), ancestor.Level));
if (ancestor.Namespace is null && ancestor.TypeName is null)
{
var styledElementType = context.GetAvaloniaTypes().StyledElement;
var ancestorType = context
.ParentNodes()
.OfType<XamlIlAstObjectNode>()
.Where(x => styledElementType.IsAssignableFrom(x.Type.GetClrType()))
.ElementAtOrDefault(ancestor.Level)
?.Type.GetClrType();
if (ancestorType is null)
{
throw new XamlIlParseException("Unable to resolve implicit ancestor type based on XAML tree.", lineInfo);
}
nodes.Add(new FindAncestorPathElementNode(ancestorType, ancestor.Level));
}
else
{
nodes.Add(new FindAncestorPathElementNode(GetType(ancestor.Namespace, ancestor.TypeName), ancestor.Level));
}
break;
case BindingExpressionGrammar.NameNode elementName:
IXamlIlType elementType = null;
@ -351,6 +386,26 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
}
}
class FindVisualAncestorPathElementNode : IXamlIlBindingPathElementNode
{
private readonly int _level;
public FindVisualAncestorPathElementNode(IXamlIlType ancestorType, int level)
{
Type = ancestorType;
_level = level;
}
public IXamlIlType Type { get; }
public void Emit(XamlIlEmitContext context, IXamlIlEmitter codeGen)
{
codeGen.Ldtype(Type)
.Ldc_I4(_level)
.EmitCall(context.GetAvaloniaTypes().CompiledBindingPathBuilder.FindMethod(m => m.Name == "VisualAncestor"));
}
}
class ElementNamePathElementNode : IXamlIlBindingPathElementNode
{
private readonly string _name;
@ -390,7 +445,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
{
codeGen.Ldsfld(_field);
context.Configuration.GetExtra<XamlIlPropertyInfoAccessorFactoryEmitter>()
.EmitLoadInpcPropertyAccessorFactory(context, codeGen);
.EmitLoadAvaloniaPropertyAccessorFactory(context, codeGen);
codeGen.EmitCall(context.GetAvaloniaTypes()
.CompiledBindingPathBuilder.FindMethod(m => m.Name == "Property"));
}

4
src/Markup/Avalonia.Markup/Markup/Parsers/BindingExpressionGrammar.cs

@ -345,6 +345,8 @@ namespace Avalonia.Markup.Parsers
public interface INode {}
public interface ITransformNode {}
public class EmptyExpressionNode : INode { }
public class PropertyNameNode : INode
@ -364,7 +366,7 @@ namespace Avalonia.Markup.Parsers
public IList<string> Arguments { get; set; }
}
public class NotNode : INode {}
public class NotNode : INode, ITransformNode {}
public class StreamNode : INode {}

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

@ -439,6 +439,61 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
Assert.Equal(dataContext.StringProperty, textBlock.Text);
}
}
[Fact]
public void ResolvesElementNameBindingFromLongForm()
{
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'>
<StackPanel>
<TextBlock Text='{CompiledBinding StringProperty}' x:Name='text' />
<TextBlock Text='{CompiledBinding Text, ElementName=text}' x:Name='text2' />
</StackPanel>
</Window>";
var loader = new AvaloniaXamlLoader();
var window = (Window)loader.Load(xaml);
var textBlock = window.FindControl<TextBlock>("text2");
var dataContext = new TestDataContext
{
StringProperty = "foobar"
};
window.DataContext = dataContext;
Assert.Equal(dataContext.StringProperty, textBlock.Text);
}
}
[Fact]
public void ResolvesRelativeSourceBindingLongForm()
{
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'
Title='test'>
<TextBlock Text='{CompiledBinding Title, RelativeSource={RelativeSource AncestorType=Window}}' x:Name='text'/>
</Window>";
var loader = new AvaloniaXamlLoader();
var window = (Window)loader.Load(xaml);
var target = window.FindControl<TextBlock>("text");
window.ApplyTemplate();
window.Presenter.ApplyTemplate();
target.ApplyTemplate();
Assert.Equal("test", target.Text);
}
}
}
public class TestDataContext

Loading…
Cancel
Save