8 changed files with 253 additions and 1 deletions
@ -0,0 +1,38 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Avalonia.Data; |
|||
|
|||
namespace Avalonia |
|||
{ |
|||
internal static class ClassBindingManager |
|||
{ |
|||
private static readonly Dictionary<string, AvaloniaProperty> s_RegisteredProperties = |
|||
new Dictionary<string, AvaloniaProperty>(); |
|||
|
|||
public static IDisposable Bind(IStyledElement target, string className, IBinding source, object anchor) |
|||
{ |
|||
if (!s_RegisteredProperties.TryGetValue(className, out var prop)) |
|||
s_RegisteredProperties[className] = prop = RegisterClassProxyProperty(className); |
|||
return target.Bind(prop, source, anchor); |
|||
} |
|||
|
|||
private static AvaloniaProperty RegisterClassProxyProperty(string className) |
|||
{ |
|||
var prop = AvaloniaProperty.Register<StyledElement, bool>("__AvaloniaReserved::Classes::" + className); |
|||
prop.Changed.Subscribe(args => |
|||
{ |
|||
var enable = args.NewValue.GetValueOrDefault(); |
|||
var classes = ((IStyledElement)args.Sender).Classes; |
|||
if (enable) |
|||
{ |
|||
if (!classes.Contains(className)) |
|||
classes.Add(className); |
|||
} |
|||
else |
|||
classes.Remove(className); |
|||
}); |
|||
|
|||
return prop; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
using System; |
|||
using Avalonia.Data; |
|||
|
|||
namespace Avalonia |
|||
{ |
|||
public static class StyledElementExtensions |
|||
{ |
|||
public static IDisposable BindClass(this IStyledElement target, string className, IBinding source, object anchor) => |
|||
ClassBindingManager.Bind(target, className, source, anchor); |
|||
} |
|||
} |
|||
@ -0,0 +1,97 @@ |
|||
using System.Collections.Generic; |
|||
using XamlX.Ast; |
|||
using XamlX.Emit; |
|||
using XamlX.IL; |
|||
using XamlX.Transform; |
|||
using XamlX.TypeSystem; |
|||
|
|||
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers |
|||
{ |
|||
class AvaloniaXamlIlResolveClassesPropertiesTransformer : IXamlAstTransformer |
|||
{ |
|||
public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node) |
|||
{ |
|||
if (node is XamlAstNamePropertyReference prop |
|||
&& prop.TargetType is XamlAstClrTypeReference targetRef |
|||
&& prop.DeclaringType is XamlAstClrTypeReference declaringRef) |
|||
{ |
|||
var types = context.GetAvaloniaTypes(); |
|||
if (types.StyledElement.IsAssignableFrom(targetRef.Type) |
|||
&& types.Classes.Equals(declaringRef.Type)) |
|||
{ |
|||
return new XamlAstClrProperty(node, "class:" + prop.Name, types.Classes, |
|||
null) |
|||
{ |
|||
Setters = { new ClassValueSetter(types, prop.Name), new ClassBindingSetter(types, prop.Name) } |
|||
}; |
|||
} |
|||
} |
|||
return node; |
|||
} |
|||
|
|||
|
|||
class ClassValueSetter : IXamlEmitablePropertySetter<IXamlILEmitter> |
|||
{ |
|||
private readonly AvaloniaXamlIlWellKnownTypes _types; |
|||
private readonly string _className; |
|||
|
|||
public ClassValueSetter(AvaloniaXamlIlWellKnownTypes types, string className) |
|||
{ |
|||
_types = types; |
|||
_className = className; |
|||
Parameters = new[] { types.XamlIlTypes.Boolean }; |
|||
} |
|||
|
|||
public void Emit(IXamlILEmitter emitter) |
|||
{ |
|||
using (var value = emitter.LocalsPool.GetLocal(_types.XamlIlTypes.Boolean)) |
|||
{ |
|||
emitter |
|||
.Stloc(value.Local) |
|||
.EmitCall(_types.StyledElementClassesProperty.Getter) |
|||
.Ldstr(_className) |
|||
.Ldloc(value.Local) |
|||
.EmitCall(_types.Classes.GetMethod(new FindMethodMethodSignature("Set", |
|||
_types.XamlIlTypes.Void, _types.XamlIlTypes.String, _types.XamlIlTypes.Boolean))); |
|||
} |
|||
} |
|||
|
|||
public IXamlType TargetType => _types.StyledElement; |
|||
|
|||
public PropertySetterBinderParameters BinderParameters { get; } = |
|||
new PropertySetterBinderParameters { AllowXNull = false }; |
|||
public IReadOnlyList<IXamlType> Parameters { get; } |
|||
} |
|||
|
|||
class ClassBindingSetter : IXamlEmitablePropertySetter<IXamlILEmitter> |
|||
{ |
|||
private readonly AvaloniaXamlIlWellKnownTypes _types; |
|||
private readonly string _className; |
|||
|
|||
public ClassBindingSetter(AvaloniaXamlIlWellKnownTypes types, string className) |
|||
{ |
|||
_types = types; |
|||
_className = className; |
|||
Parameters = new[] {types.IBinding}; |
|||
} |
|||
|
|||
public void Emit(IXamlILEmitter emitter) |
|||
{ |
|||
using (var bloc = emitter.LocalsPool.GetLocal(_types.IBinding)) |
|||
emitter |
|||
.Stloc(bloc.Local) |
|||
.Ldstr(_className) |
|||
.Ldloc(bloc.Local) |
|||
// TODO: provide anchor?
|
|||
.Ldnull(); |
|||
emitter.EmitCall(_types.ClassesBindMethod, true); |
|||
} |
|||
|
|||
public IXamlType TargetType => _types.StyledElement; |
|||
|
|||
public PropertySetterBinderParameters BinderParameters { get; } = |
|||
new PropertySetterBinderParameters { AllowXNull = false }; |
|||
public IReadOnlyList<IXamlType> Parameters { get; } |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,40 @@ |
|||
using XamlX.Ast; |
|||
using XamlX.Transform; |
|||
|
|||
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers |
|||
{ |
|||
class AvaloniaXamlIlReorderClassesPropertiesTransformer : IXamlAstTransformer |
|||
{ |
|||
public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node) |
|||
{ |
|||
if (node is XamlAstObjectNode obj) |
|||
{ |
|||
IXamlAstNode classesNode = null; |
|||
IXamlAstNode firstSingleClassNode = null; |
|||
var types = context.GetAvaloniaTypes(); |
|||
foreach (var child in obj.Children) |
|||
{ |
|||
if (child is XamlAstXamlPropertyValueNode propValue |
|||
&& propValue.Property is XamlAstClrProperty prop) |
|||
{ |
|||
if (prop.DeclaringType.Equals(types.Classes)) |
|||
{ |
|||
if (firstSingleClassNode == null) |
|||
firstSingleClassNode = child; |
|||
} |
|||
else if (prop.Name == "Classes" && prop.DeclaringType.Equals(types.StyledElement)) |
|||
classesNode = child; |
|||
} |
|||
} |
|||
|
|||
if (classesNode != null && firstSingleClassNode != null) |
|||
{ |
|||
obj.Children.Remove(classesNode); |
|||
obj.Children.Insert(obj.Children.IndexOf(firstSingleClassNode), classesNode); |
|||
} |
|||
} |
|||
|
|||
return node; |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue