diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs index 1ca7be67a7..46af0c81f5 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs @@ -65,6 +65,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions new AvaloniaXamlIlCompiledBindingsMetadataRemover() ); + Transformers.Add(new AvaloniaXamlIlControlTemplatePriorityTransformer()); Transformers.Add(new AvaloniaXamlIlMetadataRemover()); Transformers.Add(new AvaloniaXamlIlRootObjectScope()); diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlControlTemplatePriorityTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlControlTemplatePriorityTransformer.cs new file mode 100644 index 0000000000..22f1a8bc00 --- /dev/null +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlControlTemplatePriorityTransformer.cs @@ -0,0 +1,37 @@ +using System.Linq; +using XamlX.Ast; +using XamlX.Transform; + +namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers +{ + /// + /// Transforms property assignments within ControlTemplates to use Style priority where possible. + /// + class AvaloniaXamlIlControlTemplatePriorityTransformer : IXamlAstTransformer + { + public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node) + { + var bindingPriorityType = context.GetAvaloniaTypes().BindingPriority; + + if (node is XamlPropertyAssignmentNode prop && + prop.Property is XamlIlAvaloniaProperty avaloniaProperty && + context.ParentNodes().Any(IsControlTemplate)) + { + // If there are any setters which accept a binding priority, then add the Style + // binding priority as a value. + if (avaloniaProperty.Setters.Any(x => x.Parameters[0] == bindingPriorityType)) + { + prop.Values.Insert(0, new XamlConstantNode(node, bindingPriorityType, 1)); + } + } + + return node; + } + + private static bool IsControlTemplate(IXamlAstNode node) + { + return node is AvaloniaXamlIlTargetTypeMetadataNode tt && + tt.ScopeType == AvaloniaXamlIlTargetTypeMetadataNode.ScopeTypes.ControlTemplate; + } + } +} diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs index 89b88790dc..eb3b7b8f7e 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs @@ -15,6 +15,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers public IXamlType AvaloniaObjectExtensions { get; } public IXamlType AvaloniaProperty { get; } public IXamlType AvaloniaPropertyT { get; } + public IXamlType StyledPropertyT { get; } public IXamlType AvaloniaAttachedPropertyT { get; } public IXamlType IBinding { get; } public IXamlMethod AvaloniaObjectBindMethod { get; } @@ -94,6 +95,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers AvaloniaObjectExtensions = cfg.TypeSystem.GetType("Avalonia.AvaloniaObjectExtensions"); AvaloniaProperty = cfg.TypeSystem.GetType("Avalonia.AvaloniaProperty"); AvaloniaPropertyT = cfg.TypeSystem.GetType("Avalonia.AvaloniaProperty`1"); + StyledPropertyT = cfg.TypeSystem.GetType("Avalonia.StyledProperty`1"); AvaloniaAttachedPropertyT = cfg.TypeSystem.GetType("Avalonia.AttachedProperty`1"); BindingPriority = cfg.TypeSystem.GetType("Avalonia.Data.BindingPriority"); IBinding = cfg.TypeSystem.GetType("Avalonia.Data.IBinding"); diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs index 7f1b8caf0d..6c61ad0705 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs @@ -189,7 +189,13 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions CustomAttributes = original.CustomAttributes; if (!original.CustomAttributes.Any(ca => ca.Type.Equals(types.AssignBindingAttribute))) Setters.Insert(0, new BindingSetter(types, original.DeclaringType, field)); - + + if (field.FieldType.GenericTypeDefinition == types.StyledPropertyT) + { + var propertyType = field.FieldType.GenericArguments[0]; + Setters.Insert(1, new SetValueWithPrioritySetter(types, original.DeclaringType, field, propertyType)); + } + Setters.Insert(0, new UnsetValueSetter(types, original.DeclaringType, field)); } @@ -240,6 +246,39 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions } } + class SetValueWithPrioritySetter : AvaloniaPropertyCustomSetter + { + public SetValueWithPrioritySetter(AvaloniaXamlIlWellKnownTypes types, IXamlType declaringType, IXamlField avaloniaProperty, + IXamlType propertyType) + : base(types, declaringType, avaloniaProperty) + { + Parameters = new[] { types.BindingPriority, propertyType }; + } + + public override void Emit(IXamlILEmitter emitter) + { + var method = Types.AvaloniaObject + .FindMethod(m => m.IsPublic && !m.IsStatic && m.Name == "SetValue" + && m.Parameters.Count == 3 + && m.Parameters[0].Name == "StyledPropertyBase`1" + && m.Parameters[2].Equals(Types.BindingPriority)); + + if (method == null) + throw new XamlTypeSystemException( + $"Unable to find AvaloniaObject.SetValue(StyledPropertyBase, T, BindingPriority)."); + + method = method.MakeGenericMethod(new[] { Parameters[1] }); + + using (var bloc = emitter.LocalsPool.GetLocal(Parameters[1])) + emitter + .Stloc(bloc.Local) + .Ldsfld(AvaloniaProperty) + .Ldloc(bloc.Local) + .Ldc_I4(0) + .EmitCall(method, true); + } + } + class UnsetValueSetter : AvaloniaPropertyCustomSetter { public UnsetValueSetter(AvaloniaXamlIlWellKnownTypes types, IXamlType declaringType, IXamlField avaloniaProperty)