diff --git a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
index 35d34d83a8..02a7d936b6 100644
--- a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
+++ b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
@@ -55,18 +55,21 @@
+
+
+
+
-
diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs
index 8f01c71b28..5f0e84c63a 100644
--- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs
+++ b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs
@@ -28,7 +28,9 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
public string ResourceKey { get; set; }
- public override object ProvideValue(IServiceProvider serviceProvider)
+ public override object ProvideValue(IServiceProvider serviceProvider) => ProvideTypedValue(serviceProvider);
+
+ public IBinding ProvideTypedValue(IServiceProvider serviceProvider)
{
var provideTarget = serviceProvider.GetService();
diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlCompiler.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlCompiler.cs
index 768dbb7c36..490b94ea52 100644
--- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlCompiler.cs
+++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlCompiler.cs
@@ -16,6 +16,13 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
private AvaloniaXamlIlCompiler(XamlIlTransformerConfiguration configuration) : base(configuration, true)
{
+ void InsertAfter(params IXamlIlAstTransformer[] t)
+ => Transformers.InsertRange(Transformers.FindIndex(x => x is T) + 1, t);
+
+ void InsertBefore(params IXamlIlAstTransformer[] t)
+ => Transformers.InsertRange(Transformers.FindIndex(x => x is T), t);
+
+
// Before everything else
Transformers.Insert(0, new XNameTransformer());
@@ -25,21 +32,19 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
// Targeted
- Transformers.Insert(Transformers.FindIndex(x => x is XamlIlPropertyReferenceResolver),
- new AvaloniaXamlIlTransformInstanceAttachedProperties());
+ InsertBefore(new AvaloniaXamlIlTransformInstanceAttachedProperties());
+ InsertAfter(new AvaloniaXamlIlAvaloniaPropertyResolver());
- Transformers.Insert(Transformers.FindIndex(x => x is XamlIlXamlPropertyValueTransformer),
- new KnownPseudoMarkupExtensionsTransformer());
-
- Transformers.InsertRange(Transformers.FindIndex(x => x is XamlIlNewObjectTransformer),
- new IXamlIlAstTransformer[]
- {
- new AvaloniaXamlIlSelectorTransformer(),
- new AvaloniaXamlIlSetterTransformer(),
- new AvaloniaXamlIlControlTemplateTargetTypeMetadataTransformer(),
- new AvaloniaXamlIlConstructorServiceProviderTransformer()
- }
+ InsertBefore(new AvaloniaXamlIlBindingPropertyAssignmentsTransformer());
+
+
+ InsertBefore(
+ new AvaloniaXamlIlSelectorTransformer(),
+ new AvaloniaXamlIlSetterTransformer(),
+ new AvaloniaXamlIlControlTemplateTargetTypeMetadataTransformer(),
+ new AvaloniaXamlIlConstructorServiceProviderTransformer()
);
+
// After everything else
Transformers.Add(new AddNameScopeRegistration());
diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlAvaloniaPropertyResolver.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlAvaloniaPropertyResolver.cs
new file mode 100644
index 0000000000..ed17e59e14
--- /dev/null
+++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlAvaloniaPropertyResolver.cs
@@ -0,0 +1,24 @@
+using System.Linq;
+using XamlIl.Ast;
+using XamlIl.Transform;
+
+namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
+{
+ public class AvaloniaXamlIlAvaloniaPropertyResolver : IXamlIlAstTransformer
+ {
+ public IXamlIlAstNode Transform(XamlIlAstTransformationContext context, IXamlIlAstNode node)
+ {
+ if (node is XamlIlAstClrPropertyReference prop)
+ {
+ var n = prop.Property.Name + "Property";
+ var field =
+ (prop.Property.Getter ?? prop.Property.Setter).DeclaringType.Fields
+ .FirstOrDefault(f => f.Name == n);
+ if (field != null)
+ prop.Property = new XamlIlAvaloniaProperty(prop.Property, field);
+ }
+
+ return node;
+ }
+ }
+}
diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPropertyAssignmentsTransformer.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPropertyAssignmentsTransformer.cs
new file mode 100644
index 0000000000..20a394c4e0
--- /dev/null
+++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPropertyAssignmentsTransformer.cs
@@ -0,0 +1,109 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using XamlIl.Ast;
+using XamlIl.Transform;
+using XamlIl.TypeSystem;
+
+namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
+{
+ public class AvaloniaXamlIlBindingPropertyAssignmentsTransformer : IXamlIlAstTransformer
+ {
+ public IXamlIlAstNode Transform(XamlIlAstTransformationContext context, IXamlIlAstNode node)
+ {
+ if (node is XamlIlAstXamlPropertyValueNode pv
+ && pv.Property.GetClrProperty() is IXamlIlAvaloniaProperty ap
+ && pv.Values.Count == 1)
+ {
+ var types = context.GetAvaloniaTypes();
+
+ var vn = pv.Values[0];
+ if(vn.Type.GetClrType().Name.Contains("TemplateBinding"))
+ Console.WriteLine();
+ // Special handling for markup extensions
+ if (vn.Type.IsMarkupExtension)
+ {
+ if (XamlIlTransformHelpers
+ .GetMarkupExtensionProvideValueAlternatives(context, vn.Type.GetClrType())
+ .Any(x => types.IBinding.IsAssignableFrom(x.ReturnType)))
+ {
+ if (XamlIlTransformHelpers.TryConvertMarkupExtension(context, pv.Values[0],
+ new AssignBindingProperty(types, ap), out var ext))
+ return ext;
+ }
+ }
+ else if (types.IBinding.IsAssignableFrom(vn.Type.GetClrType()))
+ {
+ pv.Property = new XamlIlAstClrPropertyReference(pv.Property, new AssignBindingProperty(types, ap));
+ }
+ }
+
+ return node;
+ }
+
+ class AssignBindingProperty : IXamlIlAvaloniaProperty
+ {
+ public AssignBindingProperty(
+ AvaloniaXamlIlWellKnownTypes types,
+ IXamlIlAvaloniaProperty property
+ )
+ {
+ PropertyType = types.IBinding;
+ AvaloniaProperty = property.AvaloniaProperty;
+ CustomAttributes = property.CustomAttributes;
+ Name = property.Name;
+ Setter = new SetterMethod(types, (property.Setter ?? property.Getter).DeclaringType,
+ AvaloniaProperty);
+ }
+
+ public bool Equals(IXamlIlProperty other) =>
+ other is AssignBindingProperty abp && abp.AvaloniaProperty.Equals(AvaloniaProperty);
+
+ public string Name { get; }
+ public IXamlIlType PropertyType { get; }
+ public IXamlIlMethod Setter { get; }
+ public IXamlIlMethod Getter { get; }
+ public IReadOnlyList CustomAttributes { get; }
+ public IXamlIlField AvaloniaProperty { get; }
+
+ class SetterMethod : IXamlIlCustomEmitMethod
+ {
+ private readonly AvaloniaXamlIlWellKnownTypes _types;
+ private readonly IXamlIlField _avaloniaProperty;
+
+ public SetterMethod(AvaloniaXamlIlWellKnownTypes types,
+ IXamlIlType declaringType,
+ IXamlIlField avaloniaProperty)
+ {
+ _types = types;
+ _avaloniaProperty = avaloniaProperty;
+ Parameters = new[] {types.AvaloniaObject, types.IBinding};
+ ReturnType = types.XamlIlTypes.Void;
+ DeclaringType = declaringType;
+ Name = "Bind_" + avaloniaProperty.Name;
+ }
+
+ public bool Equals(IXamlIlMethod other) =>
+ other is SetterMethod sm && sm._avaloniaProperty.Equals(_avaloniaProperty);
+
+ public string Name { get; }
+ public bool IsPublic => true;
+ public bool IsStatic => true;
+ public IXamlIlType ReturnType { get; }
+ public IReadOnlyList Parameters { get; }
+ public IXamlIlType DeclaringType { get; }
+ public void EmitCall(IXamlIlEmitter emitter)
+ {
+ using (var bloc = emitter.LocalsPool.GetLocal(_types.IBinding))
+ emitter
+ .Stloc(bloc.Local)
+ .Ldsfld(_avaloniaProperty)
+ .Ldloc(bloc.Local)
+ // TODO: provide anchor?
+ .Ldnull();
+ emitter.EmitCall(_types.AvaloniaObjectBindMethod, true);
+ }
+ }
+ }
+ }
+}
diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlTransformInstanceAttachedProperties.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlTransformInstanceAttachedProperties.cs
index aed38dbcdf..1b3b1d7047 100644
--- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlTransformInstanceAttachedProperties.cs
+++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlTransformInstanceAttachedProperties.cs
@@ -71,7 +71,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
return node;
}
- class AvaloniaAttachedInstanceProperty : IXamlIlProperty
+ class AvaloniaAttachedInstanceProperty : IXamlIlAvaloniaProperty
{
private readonly XamlIlTransformerConfiguration _config;
private readonly IXamlIlType _declaringType;
@@ -103,6 +103,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
Getter = new GetterMethod(this);
}
+ public IXamlIlField AvaloniaProperty => _field;
public bool Equals(IXamlIlProperty other) =>
other is AvaloniaAttachedInstanceProperty ap && ap._field.Equals(_field);
diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs
new file mode 100644
index 0000000000..388bb94b35
--- /dev/null
+++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs
@@ -0,0 +1,42 @@
+using XamlIl.Transform;
+using XamlIl.TypeSystem;
+
+namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
+{
+ class AvaloniaXamlIlWellKnownTypes
+ {
+ public IXamlIlType AvaloniaObject { get; }
+ public IXamlIlType IAvaloniaObject { get; }
+ public IXamlIlType AvaloniaObjectExtensions { get; }
+ public IXamlIlType AvaloniaProperty { get; }
+ public IXamlIlType IBinding { get; }
+ public IXamlIlMethod AvaloniaObjectBindMethod { get; }
+ public IXamlIlType IDisposable { get; }
+ public XamlIlTypeWellKnownTypes XamlIlTypes { get; }
+
+ public AvaloniaXamlIlWellKnownTypes(XamlIlAstTransformationContext ctx)
+ {
+ XamlIlTypes = ctx.Configuration.WellKnownTypes;
+ AvaloniaObject = ctx.Configuration.TypeSystem.GetType("Avalonia.AvaloniaObject");
+ IAvaloniaObject = ctx.Configuration.TypeSystem.GetType("Avalonia.IAvaloniaObject");
+ AvaloniaObjectExtensions = ctx.Configuration.TypeSystem.GetType("Avalonia.AvaloniaObjectExtensions");
+ AvaloniaProperty = ctx.Configuration.TypeSystem.GetType("Avalonia.AvaloniaProperty");
+ IBinding = ctx.Configuration.TypeSystem.GetType("Avalonia.Data.IBinding");
+ IDisposable = ctx.Configuration.TypeSystem.GetType("System.IDisposable");
+ AvaloniaObjectBindMethod = AvaloniaObjectExtensions.FindMethod("Bind", IDisposable, false, IAvaloniaObject,
+ AvaloniaProperty,
+ IBinding, ctx.Configuration.WellKnownTypes.Object);
+ }
+ }
+
+ static class AvaloniaXamlIlWellKnownTypesExtensions
+ {
+ public static AvaloniaXamlIlWellKnownTypes GetAvaloniaTypes(this XamlIlAstTransformationContext ctx)
+ {
+ if (ctx.TryGetItem(out var rv))
+ return rv;
+ ctx.SetItem(rv = new AvaloniaXamlIlWellKnownTypes(ctx));
+ return rv;
+ }
+ }
+}
diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/KnownPseudoMarkupExtensionsTransformer.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/KnownPseudoMarkupExtensionsTransformer.cs
deleted file mode 100644
index de3c9ddc13..0000000000
--- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/KnownPseudoMarkupExtensionsTransformer.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-using System.Collections.Generic;
-using XamlIl.Ast;
-using XamlIl.Transform;
-
-namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
-{
- class KnownPseudoMarkupExtensionsTransformer : IXamlIlAstTransformer
- {
- private static readonly List s_knownPseudoExtensions = new List
- {
- "Avalonia.Data.TemplateBinding",
- "Avalonia.Data.MultiBinding",
- "Avalonia.Data.Binding",
- };
-
- public IXamlIlAstNode Transform(XamlIlAstTransformationContext context, IXamlIlAstNode node)
- {
- if (node is XamlIlAstXamlPropertyValueNode pn
- && pn.Values.Count == 1
- && s_knownPseudoExtensions.Contains(pn.Values[0].Type.GetClrType().FullName))
- return new XamlIlMarkupExtensionNode(node, pn.Property.GetClrProperty(),
- null, pn.Values[0], null);
- else
- return node;
- }
- }
-}
diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs
index 27c39aa8b9..e090b42d17 100644
--- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs
+++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs
@@ -15,6 +15,11 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
{
public static bool Emit(XamlIlEmitContext context, IXamlIlEmitter emitter, IXamlIlProperty property)
{
+ if (property is IXamlIlAvaloniaProperty ap)
+ {
+ emitter.Ldsfld(ap.AvaloniaProperty);
+ return true;
+ }
var type = (property.Getter ?? property.Setter).DeclaringType;
var name = property.Name + "Property";
var found = type.Fields.FirstOrDefault(f => f.IsStatic && f.Name == name);
@@ -75,4 +80,30 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
return XamlIlNodeEmitResult.Type(0, Type.GetClrType());
}
}
+
+ interface IXamlIlAvaloniaProperty : IXamlIlProperty
+ {
+ IXamlIlField AvaloniaProperty { get; }
+ }
+
+ class XamlIlAvaloniaProperty : IXamlIlAvaloniaProperty
+ {
+ private readonly IXamlIlProperty _original;
+
+ public IXamlIlField AvaloniaProperty { get; }
+ public XamlIlAvaloniaProperty(IXamlIlProperty original, IXamlIlField field)
+ {
+ _original = original;
+ AvaloniaProperty = field;
+ }
+
+ public bool Equals(IXamlIlProperty other) =>
+ other is XamlIlAvaloniaProperty p && p.AvaloniaProperty.Equals(AvaloniaProperty);
+
+ public string Name => _original.Name;
+ public IXamlIlType PropertyType => _original.PropertyType;
+ public IXamlIlMethod Setter => _original.Setter;
+ public IXamlIlMethod Getter => _original.Getter;
+ public IReadOnlyList CustomAttributes => _original.CustomAttributes;
+ }
}
diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github b/src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github
index e1ae067813..cf70f5da92 160000
--- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github
+++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github
@@ -1 +1 @@
-Subproject commit e1ae06781318a65810adb83b87ba56ffeafaf50b
+Subproject commit cf70f5da92e04e1d2d36da5451813bea2b8f54ba
diff --git a/src/Markup/Avalonia.Markup/Data/TemplateBinding.cs b/src/Markup/Avalonia.Markup/Data/TemplateBinding.cs
index fab9bdbf55..7fb8742f26 100644
--- a/src/Markup/Avalonia.Markup/Data/TemplateBinding.cs
+++ b/src/Markup/Avalonia.Markup/Data/TemplateBinding.cs
@@ -176,5 +176,7 @@ namespace Avalonia.Data
PublishValue();
}
}
+
+ public IBinding ProvideValue() => this;
}
}
diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/XamlIlBugTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/XamlIlBugTests.cs
new file mode 100644
index 0000000000..73312749c6
--- /dev/null
+++ b/tests/Avalonia.Markup.Xaml.UnitTests/XamlIlBugTests.cs
@@ -0,0 +1,39 @@
+using System.ComponentModel;
+using System.Runtime.CompilerServices;
+using Avalonia.Controls;
+using Avalonia.Media;
+using JetBrains.Annotations;
+using Xunit;
+
+namespace Avalonia.Markup.Xaml.UnitTests
+{
+ public class XamlIlBugTests
+ {
+ [Fact]
+ public void Binding_Button_IsPressed_ShouldWork()
+ {
+ var parsed = (Button)AvaloniaXamlLoader.Parse(@"
+");
+ var ctx = new XamlIlBugTestsDataContext();
+ parsed.DataContext = ctx;
+ parsed.SetValue(Button.IsPressedProperty, true);
+ Assert.True(ctx.IsPressed);
+
+ }
+
+
+ }
+
+ public class XamlIlBugTestsDataContext : INotifyPropertyChanged
+ {
+ public bool IsPressed { get; set; }
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ [NotifyPropertyChangedInvocator]
+ protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+ }
+
+}