From 351be2f4b271fded6405bb9663f3f6c5d69053f3 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sun, 20 Feb 2022 23:33:15 +0100 Subject: [PATCH] Use style priority for attached values in templates. --- ...amlIlControlTemplatePriorityTransformer.cs | 14 +++- .../XamlIlAvaloniaPropertyHelper.cs | 5 +- .../Xaml/ControlTemplateTests.cs | 66 ++++++++++++++++++- 3 files changed, 79 insertions(+), 6 deletions(-) diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlControlTemplatePriorityTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlControlTemplatePriorityTransformer.cs index fceebf860d..eede21396d 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlControlTemplatePriorityTransformer.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlControlTemplatePriorityTransformer.cs @@ -15,12 +15,19 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers { var bindingPriorityType = context.GetAvaloniaTypes().BindingPriority; + // The node is a candidate for transformation if: + // - It's a property assignment to an Avalonia property + // - There's a ControlTemplate ancestor + // - There's just a direct call setter available if (node is XamlPropertyAssignmentNode prop && prop.Property is XamlIlAvaloniaProperty avaloniaProperty && - context.ParentNodes().Any(IsControlTemplate)) + context.ParentNodes().Any(IsControlTemplate) && + prop.PossibleSetters.Count == 1 && + prop.PossibleSetters[0] is XamlDirectCallPropertySetter) { - // If there are any setters which accept a binding priority, then add the Style - // binding priority as a value. + // Check if there are any setters on the property which accept a binding priority - + // this filters the candidates down to styled and attached properties. If so, then + // use this setter with BindingPriority.Style. var setPriorityValueSetter = avaloniaProperty.Setters.FirstOrDefault(x => x.Parameters[0] == bindingPriorityType); @@ -30,6 +37,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers { prop.PossibleSetters = new List { setPriorityValueSetter }; prop.Values.Insert(0, new XamlConstantNode(node, bindingPriorityType, (int)BindingPriority.Style)); + return node; } } diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs index a568cbcef4..bb439b8816 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs @@ -190,7 +190,10 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions 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) + // Styled and attached properties can be set with a BindingPriority when they're + // assigned in a ControlTemplate. + if (field.FieldType.GenericTypeDefinition == types.StyledPropertyT || + field.FieldType.GenericTypeDefinition == types.AvaloniaAttachedPropertyT) { var propertyType = field.FieldType.GenericArguments[0]; Setters.Insert(1, new SetValueWithPrioritySetter(types, original.DeclaringType, field, propertyType)); diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlTemplateTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlTemplateTests.cs index fbb16d9f2e..f74aaaed76 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlTemplateTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlTemplateTests.cs @@ -12,7 +12,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml public class ControlTemplateTests : XamlTestBase { [Fact] - public void Inline_ControlTemplate_Values_Are_Set_With_Style_Priority() + public void Inline_ControlTemplate_Styled_Values_Are_Set_With_Style_Priority() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { @@ -43,7 +43,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml } [Fact] - public void Style_ControlTemplate_Values_Are_Set_With_Style_Priority() + public void Style_ControlTemplate_Styled_Values_Are_Set_With_Style_Priority() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { @@ -76,6 +76,68 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml } } + [Fact] + public void ControlTemplate_Attached_Values_Are_Set_With_Style_Priority() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + +"; + var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); + var button = (Button)window.Content; + + window.ApplyTemplate(); + button.ApplyTemplate(); + + var presenter = (ContentPresenter)button.Presenter; + Assert.Equal(Dock.Top, DockPanel.GetDock(presenter)); + + var diagnostic = presenter.GetDiagnostic(DockPanel.DockProperty); + Assert.Equal(BindingPriority.Style, diagnostic.Priority); + } + } + + [Fact] + public void ControlTemplate_TemplateBindings_Are_Set_With_TemplatedParent_Priority() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + +"; + var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); + var button = (Button)window.Content; + + window.ApplyTemplate(); + button.ApplyTemplate(); + + var presenter = (ContentPresenter)button.Presenter; + Assert.Equal("Foo", presenter.Content); + + var diagnostic = presenter.GetDiagnostic(ContentPresenter.ContentProperty); + Assert.Equal(BindingPriority.TemplatedParent, diagnostic.Priority); + } + } + [Fact] public void ControlTemplate_With_Nested_Child_Is_Operational() {