12 changed files with 275 additions and 44 deletions
@ -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; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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<IXamlIlCustomAttribute> 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<IXamlIlType> 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); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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<AvaloniaXamlIlWellKnownTypes>(out var rv)) |
||||
|
return rv; |
||||
|
ctx.SetItem(rv = new AvaloniaXamlIlWellKnownTypes(ctx)); |
||||
|
return rv; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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<string> s_knownPseudoExtensions = new List<string> |
|
||||
{ |
|
||||
"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; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1 +1 @@ |
|||||
Subproject commit e1ae06781318a65810adb83b87ba56ffeafaf50b |
Subproject commit cf70f5da92e04e1d2d36da5451813bea2b8f54ba |
||||
@ -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(@"
|
||||
|
<Button xmlns='https://github.com/avaloniaui' IsPressed='{Binding IsPressed, Mode=TwoWay}' />");
|
||||
|
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)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
Loading…
Reference in new issue