diff --git a/src/Avalonia.Base/PropertyStore/FramePriority.cs b/src/Avalonia.Base/PropertyStore/FramePriority.cs index 950a8375f2..f4f1ce447d 100644 --- a/src/Avalonia.Base/PropertyStore/FramePriority.cs +++ b/src/Avalonia.Base/PropertyStore/FramePriority.cs @@ -1,4 +1,5 @@ -using System.Diagnostics; +using System; +using System.Diagnostics; using Avalonia.Data; namespace Avalonia.PropertyStore @@ -8,12 +9,15 @@ namespace Avalonia.PropertyStore Animation, AnimationTemplatedParentTheme, AnimationTheme, - StyleTrigger, - StyleTriggerTemplatedParentTheme, - StyleTriggerTheme, + TemplateStyleTrigger, + TemplateStyleTriggerTemplatedParentTheme, + TemplateStyleTriggerTheme, Template, TemplateTemplatedParentTheme, TemplateTheme, + StyleTrigger, + StyleTriggerTemplatedParentTheme, + StyleTriggerTheme, Style, StyleTemplatedParentTheme, StyleTheme, @@ -21,11 +25,21 @@ namespace Avalonia.PropertyStore internal static class FramePriorityExtensions { - public static FramePriority ToFramePriority(this BindingPriority priority, FrameType type = FrameType.Style) + public static FramePriority ToFramePriority( + this BindingPriority priority, + FrameType type = FrameType.Style, + bool isTemplateSelector = false) { - Debug.Assert(priority != BindingPriority.LocalValue); - var p = (int)(priority > 0 ? priority : priority + 1); - return (FramePriority)(p * 3 + (int)type); + var p = (priority, isTemplateSelector) switch + { + (BindingPriority.Animation, _) => FramePriority.Animation, + (BindingPriority.StyleTrigger, false) => FramePriority.StyleTrigger, + (BindingPriority.StyleTrigger, true) => FramePriority.TemplateStyleTrigger, + (BindingPriority.Template, _) => FramePriority.StyleTrigger, + (BindingPriority.Style, _) => FramePriority.Style, + _ => throw new ArgumentException("Invalid priority."), + }; + return (FramePriority)((int)p + (int)type); } public static bool IsType(this FramePriority priority, FrameType type) diff --git a/src/Avalonia.Base/PropertyStore/ValueFrame.cs b/src/Avalonia.Base/PropertyStore/ValueFrame.cs index 7a9d1bb13a..498c614fe2 100644 --- a/src/Avalonia.Base/PropertyStore/ValueFrame.cs +++ b/src/Avalonia.Base/PropertyStore/ValueFrame.cs @@ -20,10 +20,10 @@ namespace Avalonia.PropertyStore private ValueStore? _owner; private bool _isShared; - protected ValueFrame(BindingPriority priority, FrameType type) + protected ValueFrame(BindingPriority priority, FrameType type, bool isTemplateSelector = false) { Priority = priority; - FramePriority = priority.ToFramePriority(type); + FramePriority = priority.ToFramePriority(type, isTemplateSelector); } public int EntryCount => _index.Count; diff --git a/src/Avalonia.Base/PropertyStore/ValueStore.cs b/src/Avalonia.Base/PropertyStore/ValueStore.cs index 2047f4d2d0..2a15dd3d24 100644 --- a/src/Avalonia.Base/PropertyStore/ValueStore.cs +++ b/src/Avalonia.Base/PropertyStore/ValueStore.cs @@ -772,7 +772,7 @@ namespace Avalonia.PropertyStore Debug.Assert(oldValue != newValue); Debug.Assert(oldValue is not null || newValue is not null); - // If the value is set locally, propagaton ends here. + // If the value is set locally, propagation ends here. if (_effectiveValues.ContainsKey(property) == true) return; @@ -825,7 +825,7 @@ namespace Avalonia.PropertyStore if (current?.Priority < priority && current?.BasePriority < priority) break; - // Try to get an entry from the frame for the property we're reevaluating. + // Try to get an entry from the frame for the property we're re-evaluating. var foundEntry = frame.TryGetEntryIfActive(property, out var entry, out var activeChanged); // If the active state of the frame has changed since the last read, and @@ -855,6 +855,9 @@ namespace Avalonia.PropertyStore AddEffectiveValue(property, current); current.SetAndRaise(this, entry, priority); } + + if (priority > BindingPriority.Animation) + break; } if (generation != _frameGeneration) diff --git a/src/Avalonia.Base/Styling/ControlTheme.cs b/src/Avalonia.Base/Styling/ControlTheme.cs index 75a3beb907..e9d802d22c 100644 --- a/src/Avalonia.Base/Styling/ControlTheme.cs +++ b/src/Avalonia.Base/Styling/ControlTheme.cs @@ -48,7 +48,7 @@ namespace Avalonia.Styling if (HasSettersOrAnimations && TargetType.IsAssignableFrom(StyledElement.GetStyleKey(target))) { - Attach(target, null, type); + Attach(target, null, type, false); return SelectorMatchResult.AlwaysThisType; } diff --git a/src/Avalonia.Base/Styling/Style.cs b/src/Avalonia.Base/Styling/Style.cs index a5d89392e9..7b34feb8fd 100644 --- a/src/Avalonia.Base/Styling/Style.cs +++ b/src/Avalonia.Base/Styling/Style.cs @@ -74,7 +74,7 @@ namespace Avalonia.Styling if (match.IsMatch) { - Attach(target, match.Activator, type); + Attach(target, match.Activator, type, Selector?.InTemplate ?? false); } result = match.Result; diff --git a/src/Avalonia.Base/Styling/StyleBase.cs b/src/Avalonia.Base/Styling/StyleBase.cs index 318e8d6890..907ebe1b4f 100644 --- a/src/Avalonia.Base/Styling/StyleBase.cs +++ b/src/Avalonia.Base/Styling/StyleBase.cs @@ -92,7 +92,7 @@ namespace Avalonia.Styling return false; } - internal ValueFrame Attach(StyledElement target, IStyleActivator? activator, FrameType type) + internal ValueFrame Attach(StyledElement target, IStyleActivator? activator, FrameType type, bool isTemplateSelector) { if (target is not AvaloniaObject ao) throw new InvalidOperationException("Styles can only be applied to AvaloniaObjects."); @@ -107,7 +107,7 @@ namespace Avalonia.Styling { var canShareInstance = activator is null; - instance = new StyleInstance(this, activator, type); + instance = new StyleInstance(this, activator, type, isTemplateSelector); if (_setters is not null) { diff --git a/src/Avalonia.Base/Styling/StyleInstance.cs b/src/Avalonia.Base/Styling/StyleInstance.cs index 61cb31c6d0..d6808e46a6 100644 --- a/src/Avalonia.Base/Styling/StyleInstance.cs +++ b/src/Avalonia.Base/Styling/StyleInstance.cs @@ -29,8 +29,9 @@ namespace Avalonia.Styling public StyleInstance( IStyle style, IStyleActivator? activator, - FrameType type) - : base(GetPriority(activator), type) + FrameType type, + bool isTemplateSelector) + : base(GetPriority(activator), type, isTemplateSelector) { _activator = activator; Source = style; diff --git a/tests/Avalonia.Base.UnitTests/PropertyStore/ValueStoreTests_Frames.cs b/tests/Avalonia.Base.UnitTests/PropertyStore/ValueStoreTests_Frames.cs index 3a307447ac..abc2bd398d 100644 --- a/tests/Avalonia.Base.UnitTests/PropertyStore/ValueStoreTests_Frames.cs +++ b/tests/Avalonia.Base.UnitTests/PropertyStore/ValueStoreTests_Frames.cs @@ -117,7 +117,7 @@ namespace Avalonia.Base.UnitTests.PropertyStore private static StyleInstance InstanceStyle(Style style, StyledElement target) { - var result = new StyleInstance(style, null, FrameType.Style); + var result = new StyleInstance(style, null, FrameType.Style, false); foreach (var setter in style.Setters) result.Add(setter.Instance(result, target)); diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlTemplateTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlTemplateTests.cs index e21f6cd276..08c5c77e1c 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlTemplateTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlTemplateTests.cs @@ -5,6 +5,7 @@ using Avalonia.Controls.Presenters; using Avalonia.Controls.Primitives; using Avalonia.Data; using Avalonia.Diagnostics; +using Avalonia.Layout; using Avalonia.Markup.Xaml.Templates; using Avalonia.Media; using Avalonia.UnitTests; @@ -246,6 +247,41 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml } } + [Fact] + public void Foo() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + + + + + + + + + +