Browse Source
Implement x:CompileBindings directive to toggle between transforming Binding->ReflectionBindingExtension and Binding->CompiledBindingExtension. Expose a property on AvaloniaXamlIlCompiler to set the default transformation.pull/2734/head
9 changed files with 163 additions and 36 deletions
@ -1,20 +0,0 @@ |
|||||
using XamlIl.Ast; |
|
||||
using XamlIl.Transform; |
|
||||
|
|
||||
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers |
|
||||
{ |
|
||||
class AvaloniaBindingExtensionHackTransformer : IXamlIlAstTransformer |
|
||||
{ |
|
||||
public IXamlIlAstNode Transform(XamlIlAstTransformationContext context, IXamlIlAstNode node) |
|
||||
{ |
|
||||
// Our code base expects XAML parser to prefer `FooExtension` to `Foo` even with `<Foo>` syntax
|
|
||||
// This is the legacy of Portable.Xaml, so we emulate that behavior here
|
|
||||
|
|
||||
if (node is XamlIlAstXmlTypeReference tref |
|
||||
&& tref.Name == "Binding" |
|
||||
&& tref.XmlNamespace == "https://github.com/avaloniaui") |
|
||||
tref.IsMarkupExtension = true; |
|
||||
return node; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,73 @@ |
|||||
|
using System.Linq; |
||||
|
using XamlIl; |
||||
|
using XamlIl.Ast; |
||||
|
using XamlIl.Transform; |
||||
|
|
||||
|
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers |
||||
|
{ |
||||
|
class AvaloniaBindingExtensionTransformer : IXamlIlAstTransformer |
||||
|
{ |
||||
|
public bool CompileBindingsByDefault { get; set; } |
||||
|
|
||||
|
public IXamlIlAstNode Transform(XamlIlAstTransformationContext context, IXamlIlAstNode node) |
||||
|
{ |
||||
|
if (context.ParentNodes().FirstOrDefault() is AvaloniaXamlIlCompileBindingsNode) |
||||
|
{ |
||||
|
return node; |
||||
|
} |
||||
|
|
||||
|
if (node is XamlIlAstObjectNode obj) |
||||
|
{ |
||||
|
foreach (var item in obj.Children) |
||||
|
{ |
||||
|
if (item is XamlIlAstXmlDirective directive) |
||||
|
{ |
||||
|
if (directive.Namespace == XamlNamespaces.Xaml2006 |
||||
|
&& directive.Name == "CompileBindings" |
||||
|
&& directive.Values.Count == 1) |
||||
|
{ |
||||
|
if (!(directive.Values[0] is XamlIlAstTextNode text |
||||
|
&& bool.TryParse(text.Text, out var compileBindings))) |
||||
|
{ |
||||
|
throw new XamlIlParseException("The value of x:CompileBindings must be a literal boolean value.", directive.Values[0]); |
||||
|
} |
||||
|
|
||||
|
obj.Children.Remove(directive); |
||||
|
|
||||
|
return new AvaloniaXamlIlCompileBindingsNode(obj, compileBindings); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Our code base expects XAML parser to prefer `FooExtension` to `Foo` even with `<Foo>` syntax
|
||||
|
// This is the legacy of Portable.Xaml, so we emulate that behavior here
|
||||
|
|
||||
|
if (node is XamlIlAstXmlTypeReference tref |
||||
|
&& tref.Name == "Binding" |
||||
|
&& tref.XmlNamespace == "https://github.com/avaloniaui") |
||||
|
{ |
||||
|
tref.IsMarkupExtension = true; |
||||
|
|
||||
|
var compileBindings = context.ParentNodes() |
||||
|
.OfType<AvaloniaXamlIlCompileBindingsNode>() |
||||
|
.FirstOrDefault() |
||||
|
?.CompileBindings ?? CompileBindingsByDefault; |
||||
|
|
||||
|
tref.Name = compileBindings ? "CompiledBinding" : "ReflectionBinding"; |
||||
|
} |
||||
|
return node; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
internal class AvaloniaXamlIlCompileBindingsNode : XamlIlValueWithSideEffectNodeBase |
||||
|
{ |
||||
|
public AvaloniaXamlIlCompileBindingsNode(IXamlIlAstValueNode value, bool compileBindings) |
||||
|
: base(value, value) |
||||
|
{ |
||||
|
CompileBindings = compileBindings; |
||||
|
} |
||||
|
|
||||
|
public bool CompileBindings { get; } |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue