Browse Source

Support synthetic Source property.

pull/2734/head
Jeremy Koritzinsky 6 years ago
parent
commit
5e5e7e22ae
  1. 7
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindingExtension.cs
  2. 14
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CompiledBindingPath.cs
  3. 48
      src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathParser.cs
  4. 8
      src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlTransformSyntheticCompiledBindingMembers.cs
  5. 43
      src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/XamlIlBindingPathHelper.cs
  6. 25
      tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs

7
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindingExtension.cs

@ -50,6 +50,13 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
protected override ExpressionObserver CreateExpressionObserver(IAvaloniaObject target, AvaloniaProperty targetProperty, object anchor, bool enableDataValidation)
{
if (Path.RawSource != null)
{
return CreateSourceObserver(
Path.RawSource,
Path.BuildExpression(enableDataValidation));
}
if (Path.SourceMode == SourceMode.Data)
{
return CreateDataContextObserver(

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

@ -14,9 +14,10 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
public CompiledBindingPath() { }
internal CompiledBindingPath(IEnumerable<ICompiledBindingPathElement> bindingPath)
internal CompiledBindingPath(IEnumerable<ICompiledBindingPathElement> bindingPath, object rawSource)
{
_elements = new List<ICompiledBindingPathElement>(bindingPath);
RawSource = rawSource;
}
public ExpressionNode BuildExpression(bool enableValidation)
@ -63,10 +64,13 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
}
internal SourceMode SourceMode => _elements.Count > 0 && _elements[0] is IControlSourceBindingPathElement ? SourceMode.Control : SourceMode.Data;
internal object RawSource { get; }
}
public class CompiledBindingPathBuilder
{
private object _rawSource;
private List<ICompiledBindingPathElement> _elements = new List<ICompiledBindingPathElement>();
public CompiledBindingPathBuilder Not()
@ -122,7 +126,13 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
return this;
}
public CompiledBindingPath Build() => new CompiledBindingPath(_elements);
public CompiledBindingPathBuilder SetRawSource(object rawSource)
{
_rawSource = rawSource;
return this;
}
public CompiledBindingPath Build() => new CompiledBindingPath(_elements, _rawSource);
}
public interface ICompiledBindingPathElement

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

@ -70,6 +70,11 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
v.Property is AvaloniaSyntheticCompiledBindingProperty prop
&& prop.Name == SyntheticCompiledBindingPropertyName.ElementName);
var sourceProperty = syntheticCompiledBindingProperties
.FirstOrDefault(v =>
v.Property is AvaloniaSyntheticCompiledBindingProperty prop
&& prop.Name == SyntheticCompiledBindingPropertyName.Source);
var relativeSourceProperty = syntheticCompiledBindingProperties
.FirstOrDefault(v =>
v.Property is AvaloniaSyntheticCompiledBindingProperty prop
@ -84,6 +89,16 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
throw new XamlIlParseException($"Invalid ElementName '{elementNameProperty.Values[0]}'.", elementNameProperty.Values[0]);
}
if (sourceProperty?.Values[0] != null)
{
if (convertedNode != null)
{
throw new XamlIlParseException("Only one of ElementName, Source, or RelativeSource specified as a binding source. Only one property is allowed.", binding);
}
convertedNode = new RawSourceBindingExpressionNode(sourceProperty?.Values[0]);
}
if (GetRelativeSourceObjectFromAssignment(
context,
relativeSourceProperty,
@ -91,7 +106,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
{
if (convertedNode != null)
{
throw new XamlIlParseException("Both ElementName and RelativeSource specified as a binding source. Only one property is allowed.", binding);
throw new XamlIlParseException("Only one of ElementName, Source, or RelativeSource specified as a binding source. Only one property is allowed.", binding);
}
var mode = relativeSourceObject.Children
@ -206,6 +221,10 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
{
binding.Children.Remove(elementNameProperty);
}
if (sourceProperty != null)
{
binding.Children.Remove(sourceProperty);
}
if (relativeSourceProperty != null)
{
binding.Children.Remove(relativeSourceProperty);
@ -263,6 +282,17 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
public IXamlIlAstTypeReference Type { get; }
public IList<BindingExpressionGrammar.INode> Path { get; }
public override void VisitChildren(IXamlIlAstVisitor visitor)
{
for (int i = 0; i < Path.Count; i++)
{
if (Path[i] is IXamlIlAstNode ast)
{
Path[i] = (BindingExpressionGrammar.INode)ast.Visit(visitor);
}
}
}
}
class VisualAncestorBindingExpressionNode : BindingExpressionGrammar.INode
@ -281,4 +311,20 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
{
public IXamlIlType Type { get; set; }
}
class RawSourceBindingExpressionNode : XamlIlAstNode, BindingExpressionGrammar.INode
{
public RawSourceBindingExpressionNode(IXamlIlAstValueNode rawSource)
: base(rawSource)
{
RawSource = rawSource;
}
public IXamlIlAstValueNode RawSource { get; private set; }
public override void VisitChildren(IXamlIlAstVisitor visitor)
{
RawSource = (IXamlIlAstValueNode)RawSource.Visit(visitor);
}
}
}

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

@ -25,6 +25,11 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
return new AvaloniaSyntheticCompiledBindingProperty(node,
SyntheticCompiledBindingPropertyName.RelativeSource);
}
else if (prop.Name == "Source")
{
return new AvaloniaSyntheticCompiledBindingProperty(node,
SyntheticCompiledBindingPropertyName.Source);
}
}
return node;
@ -34,7 +39,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
enum SyntheticCompiledBindingPropertyName
{
ElementName,
RelativeSource
RelativeSource,
Source
}
class AvaloniaSyntheticCompiledBindingProperty : XamlIlAstNode, IXamlIlAstPropertyReference

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

@ -235,6 +235,9 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
}
nodes.Add(new ElementNamePathElementNode(elementName.Name, elementType));
break;
case RawSourceBindingExpressionNode rawSource:
nodes.Add(new RawSourcePathElementNode(rawSource.RawSource));
break;
}
}
@ -563,6 +566,28 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
public IXamlIlType Type => _arrayType.ArrayElementType;
}
class RawSourcePathElementNode : XamlIlAstNode, IXamlIlBindingPathElementNode
{
private readonly IXamlIlAstValueNode _rawSource;
public RawSourcePathElementNode(IXamlIlAstValueNode rawSource)
:base(rawSource)
{
_rawSource = rawSource;
}
public IXamlIlType Type => _rawSource.Type.GetClrType();
public void Emit(XamlIlEmitContext context, IXamlIlEmitter codeGen)
{
context.Emit(_rawSource, codeGen, Type);
codeGen
.EmitCall(context.GetAvaloniaTypes()
.CompiledBindingPathBuilder.FindMethod(m => m.Name == "SetRawSource"));
}
}
class XamlIlBindingPathNode : XamlIlAstNode, IXamlIlBindingPathNode, IXamlIlAstEmitableNode
{
private readonly List<IXamlIlBindingPathElementNode> _transformElements;
@ -603,6 +628,24 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
codeGen.EmitCall(types.CompiledBindingPathBuilder.FindMethod(m => m.Name == "Build"));
return XamlIlNodeEmitResult.Type(0, types.CompiledBindingPath);
}
public override void VisitChildren(IXamlIlAstVisitor visitor)
{
for (int i = 0; i < _transformElements.Count; i++)
{
if (_transformElements[i] is IXamlIlAstNode ast)
{
_transformElements[i] = (IXamlIlBindingPathElementNode)ast.Visit(visitor);
}
}
for (int i = 0; i < _elements.Count; i++)
{
if (_elements[i] is IXamlIlAstNode ast)
{
_elements[i] = (IXamlIlBindingPathElementNode)ast.Visit(visitor);
}
}
}
}
}

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

@ -494,6 +494,31 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
Assert.Equal("test", target.Text);
}
}
[Fact]
public void ResolvesSourceBindingLongForm()
{
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 Length, Source=Test}' 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".Length.ToString(), target.Text);
}
}
}
public class TestDataContext

Loading…
Cancel
Save