From ca02947c3ee9d24782146e2e784c50b84a16ca5c Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 18 May 2023 10:35:08 +0200 Subject: [PATCH 1/5] Added failing test for VirtualizingStackPanel. --- .../VirtualizingStackPanelTests.cs | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/tests/Avalonia.Controls.UnitTests/VirtualizingStackPanelTests.cs b/tests/Avalonia.Controls.UnitTests/VirtualizingStackPanelTests.cs index aa03a77d70..1fddaab910 100644 --- a/tests/Avalonia.Controls.UnitTests/VirtualizingStackPanelTests.cs +++ b/tests/Avalonia.Controls.UnitTests/VirtualizingStackPanelTests.cs @@ -657,6 +657,40 @@ namespace Avalonia.Controls.UnitTests root.LayoutManager.ExecuteLayoutPass(); } + [Fact] + public void ScrollIntoView_On_Effectively_Invisible_Panel_Does_Not_Create_Ghost_Elements() + { + var items = new[] { "foo", "bar", "baz" }; + var (target, _, itemsControl) = CreateUnrootedTarget(items: items); + var container = new Decorator { Margin = new Thickness(100), Child = itemsControl }; + var root = new TestRoot(true, container); + + root.LayoutManager.ExecuteInitialLayoutPass(); + + // Clear the items and do a layout to recycle all elements. + itemsControl.ItemsSource = null; + root.LayoutManager.ExecuteLayoutPass(); + + // Should have no realized elements and 3 unrealized elements. + Assert.Equal(0, target.GetRealizedElements().Count); + Assert.Equal(3, target.Children.Count); + + // Make the panel effectively invisible and set items. + container.IsVisible = false; + itemsControl.ItemsSource = items; + + // Try to scroll into view while effectively invisible. + target.ScrollIntoView(0); + + // Make the panel visible and layout. + container.IsVisible = true; + root.LayoutManager.ExecuteLayoutPass(); + + // Should have 3 realized elements and no unrealized elements. + Assert.Equal(3, target.GetRealizedElements().Count); + Assert.Equal(3, target.Children.Count); + } + private static IReadOnlyList GetRealizedIndexes(VirtualizingStackPanel target, ItemsControl itemsControl) { return target.GetRealizedElements() From 5a718074049b3e0e8dc7ba235ba00ade3dec0249 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 18 May 2023 10:51:54 +0200 Subject: [PATCH 2/5] Don't try to scroll into view on invisible panel. It won't work because layout won't be run, and will result in a ghost element. --- src/Avalonia.Controls/VirtualizingStackPanel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/VirtualizingStackPanel.cs b/src/Avalonia.Controls/VirtualizingStackPanel.cs index 7ec1808e63..0580761631 100644 --- a/src/Avalonia.Controls/VirtualizingStackPanel.cs +++ b/src/Avalonia.Controls/VirtualizingStackPanel.cs @@ -343,7 +343,7 @@ namespace Avalonia.Controls { var items = Items; - if (_isInLayout || index < 0 || index >= items.Count || _realizedElements is null) + if (_isInLayout || index < 0 || index >= items.Count || _realizedElements is null || !IsEffectivelyVisible) return null; if (GetRealizedElement(index) is Control element) From 04c50f8bf26c3c867fb6f9a6b2349ef0752e2dad Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 18 May 2023 13:12:15 +0200 Subject: [PATCH 3/5] Make style selector internals internal. --- .../Styling/Activators/IStyleActivator.cs | 3 +- .../Styling/Activators/IStyleActivatorSink.cs | 3 +- src/Avalonia.Base/Styling/ChildSelector.cs | 12 +++---- .../Styling/DescendentSelector.cs | 12 +++---- src/Avalonia.Base/Styling/NestingSelector.cs | 12 +++---- src/Avalonia.Base/Styling/NotSelector.cs | 12 +++---- src/Avalonia.Base/Styling/NthChildSelector.cs | 14 ++++---- .../Styling/NthLastChildSelector.cs | 2 +- src/Avalonia.Base/Styling/OrSelector.cs | 12 +++---- .../Styling/PropertyEqualsSelector.cs | 12 +++---- src/Avalonia.Base/Styling/Selector.cs | 14 ++++---- src/Avalonia.Base/Styling/SelectorMatch.cs | 4 +-- src/Avalonia.Base/Styling/TemplateSelector.cs | 12 +++---- .../Styling/TypeNameAndClassSelector.cs | 12 +++---- .../Styling/SelectorBenchmark.cs | 36 +++++++++---------- tests/Avalonia.UnitTests/StyleHelpers.cs | 4 +-- 16 files changed, 87 insertions(+), 89 deletions(-) diff --git a/src/Avalonia.Base/Styling/Activators/IStyleActivator.cs b/src/Avalonia.Base/Styling/Activators/IStyleActivator.cs index 487198a861..7bc0777c61 100644 --- a/src/Avalonia.Base/Styling/Activators/IStyleActivator.cs +++ b/src/Avalonia.Base/Styling/Activators/IStyleActivator.cs @@ -15,8 +15,7 @@ namespace Avalonia.Styling.Activators /// - The activation state can be re-evaluated at any time by calling /// - No error or completion messages /// - [Unstable] - public interface IStyleActivator : IDisposable + internal interface IStyleActivator : IDisposable { /// /// Gets a value indicating whether the style is subscribed. diff --git a/src/Avalonia.Base/Styling/Activators/IStyleActivatorSink.cs b/src/Avalonia.Base/Styling/Activators/IStyleActivatorSink.cs index 142a3c3517..6d49485c20 100644 --- a/src/Avalonia.Base/Styling/Activators/IStyleActivatorSink.cs +++ b/src/Avalonia.Base/Styling/Activators/IStyleActivatorSink.cs @@ -5,8 +5,7 @@ namespace Avalonia.Styling.Activators /// /// Receives notifications from an . /// - [Unstable] - public interface IStyleActivatorSink + internal interface IStyleActivatorSink { /// /// Called when the subscribed activator value changes. diff --git a/src/Avalonia.Base/Styling/ChildSelector.cs b/src/Avalonia.Base/Styling/ChildSelector.cs index 400ba18530..ac28d2bc46 100644 --- a/src/Avalonia.Base/Styling/ChildSelector.cs +++ b/src/Avalonia.Base/Styling/ChildSelector.cs @@ -19,13 +19,13 @@ namespace Avalonia.Styling } /// - public override bool InTemplate => _parent.InTemplate; + internal override bool InTemplate => _parent.InTemplate; /// - public override bool IsCombinator => true; + internal override bool IsCombinator => true; /// - public override Type? TargetType => null; + internal override Type? TargetType => null; public override string ToString(Style? owner) { @@ -37,7 +37,7 @@ namespace Avalonia.Styling return _selectorString; } - protected override SelectorMatch Evaluate(StyledElement control, IStyle? parent, bool subscribe) + private protected override SelectorMatch Evaluate(StyledElement control, IStyle? parent, bool subscribe) { var controlParent = ((ILogical)control).LogicalParent; @@ -64,7 +64,7 @@ namespace Avalonia.Styling } } - protected override Selector? MovePrevious() => null; - protected override Selector? MovePreviousOrParent() => _parent; + private protected override Selector? MovePrevious() => null; + private protected override Selector? MovePreviousOrParent() => _parent; } } diff --git a/src/Avalonia.Base/Styling/DescendentSelector.cs b/src/Avalonia.Base/Styling/DescendentSelector.cs index 20874a6877..6706eb4441 100644 --- a/src/Avalonia.Base/Styling/DescendentSelector.cs +++ b/src/Avalonia.Base/Styling/DescendentSelector.cs @@ -17,13 +17,13 @@ namespace Avalonia.Styling } /// - public override bool IsCombinator => true; + internal override bool IsCombinator => true; /// - public override bool InTemplate => _parent.InTemplate; + internal override bool InTemplate => _parent.InTemplate; /// - public override Type? TargetType => null; + internal override Type? TargetType => null; public override string ToString(Style? owner) { @@ -35,7 +35,7 @@ namespace Avalonia.Styling return _selectorString; } - protected override SelectorMatch Evaluate(StyledElement control, IStyle? parent, bool subscribe) + private protected override SelectorMatch Evaluate(StyledElement control, IStyle? parent, bool subscribe) { var c = (ILogical)control; var descendantMatches = new OrActivatorBuilder(); @@ -69,7 +69,7 @@ namespace Avalonia.Styling } } - protected override Selector? MovePrevious() => null; - protected override Selector? MovePreviousOrParent() => _parent; + private protected override Selector? MovePrevious() => null; + private protected override Selector? MovePreviousOrParent() => _parent; } } diff --git a/src/Avalonia.Base/Styling/NestingSelector.cs b/src/Avalonia.Base/Styling/NestingSelector.cs index deb688ca4d..4a6b0cfda9 100644 --- a/src/Avalonia.Base/Styling/NestingSelector.cs +++ b/src/Avalonia.Base/Styling/NestingSelector.cs @@ -7,13 +7,13 @@ namespace Avalonia.Styling /// internal class NestingSelector : Selector { - public override bool InTemplate => false; - public override bool IsCombinator => false; - public override Type? TargetType => null; + internal override bool InTemplate => false; + internal override bool IsCombinator => false; + internal override Type? TargetType => null; public override string ToString(Style? owner) => owner?.Parent?.ToString() ?? "^"; - protected override SelectorMatch Evaluate(StyledElement control, IStyle? parent, bool subscribe) + private protected override SelectorMatch Evaluate(StyledElement control, IStyle? parent, bool subscribe) { if (parent is Style s && s.Selector is not null) { @@ -32,7 +32,7 @@ namespace Avalonia.Styling "Nesting selector was specified but cannot determine parent selector."); } - protected override Selector? MovePrevious() => null; - protected override Selector? MovePreviousOrParent() => null; + private protected override Selector? MovePrevious() => null; + private protected override Selector? MovePreviousOrParent() => null; } } diff --git a/src/Avalonia.Base/Styling/NotSelector.cs b/src/Avalonia.Base/Styling/NotSelector.cs index f6b1288ac5..9a541cbba7 100644 --- a/src/Avalonia.Base/Styling/NotSelector.cs +++ b/src/Avalonia.Base/Styling/NotSelector.cs @@ -26,13 +26,13 @@ namespace Avalonia.Styling } /// - public override bool InTemplate => _argument.InTemplate; + internal override bool InTemplate => _argument.InTemplate; /// - public override bool IsCombinator => false; + internal override bool IsCombinator => false; /// - public override Type? TargetType => _previous?.TargetType; + internal override Type? TargetType => _previous?.TargetType; /// public override string ToString(Style? owner) @@ -45,7 +45,7 @@ namespace Avalonia.Styling return _selectorString; } - protected override SelectorMatch Evaluate(StyledElement control, IStyle? parent, bool subscribe) + private protected override SelectorMatch Evaluate(StyledElement control, IStyle? parent, bool subscribe) { var innerResult = _argument.Match(control, parent, subscribe); @@ -66,7 +66,7 @@ namespace Avalonia.Styling } } - protected override Selector? MovePrevious() => _previous; - protected override Selector? MovePreviousOrParent() => _previous; + private protected override Selector? MovePrevious() => _previous; + private protected override Selector? MovePreviousOrParent() => _previous; } } diff --git a/src/Avalonia.Base/Styling/NthChildSelector.cs b/src/Avalonia.Base/Styling/NthChildSelector.cs index 532179bb2c..bf6247aba1 100644 --- a/src/Avalonia.Base/Styling/NthChildSelector.cs +++ b/src/Avalonia.Base/Styling/NthChildSelector.cs @@ -12,7 +12,7 @@ namespace Avalonia.Styling /// /// Element indices are 1-based. /// - public class NthChildSelector : Selector + internal class NthChildSelector : Selector { private const string NthChildSelectorName = "nth-child"; private const string NthLastChildSelectorName = "nth-last-child"; @@ -39,16 +39,16 @@ namespace Avalonia.Styling } - public override bool InTemplate => _previous?.InTemplate ?? false; + internal override bool InTemplate => _previous?.InTemplate ?? false; - public override bool IsCombinator => false; + internal override bool IsCombinator => false; - public override Type? TargetType => _previous?.TargetType; + internal override Type? TargetType => _previous?.TargetType; public int Step { get; } public int Offset { get; } - protected override SelectorMatch Evaluate(StyledElement control, IStyle? parent, bool subscribe) + private protected override SelectorMatch Evaluate(StyledElement control, IStyle? parent, bool subscribe) { if (!(control is ILogical logical)) { @@ -103,8 +103,8 @@ namespace Avalonia.Styling return match ? SelectorMatch.AlwaysThisInstance : SelectorMatch.NeverThisInstance; } - protected override Selector? MovePrevious() => _previous; - protected override Selector? MovePreviousOrParent() => _previous; + private protected override Selector? MovePrevious() => _previous; + private protected override Selector? MovePreviousOrParent() => _previous; public override string ToString(Style? owner) { diff --git a/src/Avalonia.Base/Styling/NthLastChildSelector.cs b/src/Avalonia.Base/Styling/NthLastChildSelector.cs index 6f6abbae6a..aa62ad2b2c 100644 --- a/src/Avalonia.Base/Styling/NthLastChildSelector.cs +++ b/src/Avalonia.Base/Styling/NthLastChildSelector.cs @@ -8,7 +8,7 @@ namespace Avalonia.Styling /// /// Element indices are 1-based. /// - public class NthLastChildSelector : NthChildSelector + internal class NthLastChildSelector : NthChildSelector { /// /// Creates an instance of diff --git a/src/Avalonia.Base/Styling/OrSelector.cs b/src/Avalonia.Base/Styling/OrSelector.cs index 53e4baa2c4..cc77aa9fcf 100644 --- a/src/Avalonia.Base/Styling/OrSelector.cs +++ b/src/Avalonia.Base/Styling/OrSelector.cs @@ -36,13 +36,13 @@ namespace Avalonia.Styling } /// - public override bool InTemplate => false; + internal override bool InTemplate => false; /// - public override bool IsCombinator => false; + internal override bool IsCombinator => false; /// - public override Type? TargetType => _targetType ??= EvaluateTargetType(); + internal override Type? TargetType => _targetType ??= EvaluateTargetType(); /// public override string ToString(Style? owner) @@ -55,7 +55,7 @@ namespace Avalonia.Styling return _selectorString; } - protected override SelectorMatch Evaluate(StyledElement control, IStyle? parent, bool subscribe) + private protected override SelectorMatch Evaluate(StyledElement control, IStyle? parent, bool subscribe) { var activators = new OrActivatorBuilder(); var neverThisInstance = false; @@ -94,8 +94,8 @@ namespace Avalonia.Styling } } - protected override Selector? MovePrevious() => null; - protected override Selector? MovePreviousOrParent() => null; + private protected override Selector? MovePrevious() => null; + private protected override Selector? MovePreviousOrParent() => null; internal override void ValidateNestingSelector(bool inControlTheme) { diff --git a/src/Avalonia.Base/Styling/PropertyEqualsSelector.cs b/src/Avalonia.Base/Styling/PropertyEqualsSelector.cs index 0865a7a8b0..3a50923094 100644 --- a/src/Avalonia.Base/Styling/PropertyEqualsSelector.cs +++ b/src/Avalonia.Base/Styling/PropertyEqualsSelector.cs @@ -28,13 +28,13 @@ namespace Avalonia.Styling } /// - public override bool InTemplate => _previous?.InTemplate ?? false; + internal override bool InTemplate => _previous?.InTemplate ?? false; /// - public override bool IsCombinator => false; + internal override bool IsCombinator => false; /// - public override Type? TargetType => _previous?.TargetType; + internal override Type? TargetType => _previous?.TargetType; /// public override string ToString(Style? owner) @@ -73,7 +73,7 @@ namespace Avalonia.Styling } /// - protected override SelectorMatch Evaluate(StyledElement control, IStyle? parent, bool subscribe) + private protected override SelectorMatch Evaluate(StyledElement control, IStyle? parent, bool subscribe) { if (subscribe) { @@ -88,8 +88,8 @@ namespace Avalonia.Styling } - protected override Selector? MovePrevious() => _previous; - protected override Selector? MovePreviousOrParent() => _previous; + private protected override Selector? MovePrevious() => _previous; + private protected override Selector? MovePreviousOrParent() => _previous; [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = TrimmingMessages.TypeConvertionSupressWarningMessage)] [UnconditionalSuppressMessage("Trimming", "IL2067", Justification = TrimmingMessages.TypeConvertionSupressWarningMessage)] diff --git a/src/Avalonia.Base/Styling/Selector.cs b/src/Avalonia.Base/Styling/Selector.cs index c83950f72d..4e1c338553 100644 --- a/src/Avalonia.Base/Styling/Selector.cs +++ b/src/Avalonia.Base/Styling/Selector.cs @@ -14,7 +14,7 @@ namespace Avalonia.Styling /// Gets a value indicating whether either this selector or a previous selector has moved /// into a template. /// - public abstract bool InTemplate { get; } + internal abstract bool InTemplate { get; } /// /// Gets a value indicating whether this selector is a combinator. @@ -22,12 +22,12 @@ namespace Avalonia.Styling /// /// A combinator is a selector such as Child or Descendent which links simple selectors. /// - public abstract bool IsCombinator { get; } + internal abstract bool IsCombinator { get; } /// /// Gets the target type of the selector, if available. /// - public abstract Type? TargetType { get; } + internal abstract Type? TargetType { get; } /// /// Tries to match the selector with a control. @@ -41,7 +41,7 @@ namespace Avalonia.Styling /// or simply return an immediate result. /// /// A . - public SelectorMatch Match(StyledElement control, IStyle? parent = null, bool subscribe = true) + internal SelectorMatch Match(StyledElement control, IStyle? parent = null, bool subscribe = true) { // First match the selector until a combinator is found. Selectors are stored from // right-to-left, so MatchUntilCombinator reverses this order because the type selector @@ -88,17 +88,17 @@ namespace Avalonia.Styling /// or simply return an immediate result. /// /// A . - protected abstract SelectorMatch Evaluate(StyledElement control, IStyle? parent, bool subscribe); + private protected abstract SelectorMatch Evaluate(StyledElement control, IStyle? parent, bool subscribe); /// /// Moves to the previous selector. /// - protected abstract Selector? MovePrevious(); + private protected abstract Selector? MovePrevious(); /// /// Moves to the previous selector or the parent selector. /// - protected abstract Selector? MovePreviousOrParent(); + private protected abstract Selector? MovePreviousOrParent(); internal virtual void ValidateNestingSelector(bool inControlTheme) { diff --git a/src/Avalonia.Base/Styling/SelectorMatch.cs b/src/Avalonia.Base/Styling/SelectorMatch.cs index 2eac04301a..cbcab612d6 100644 --- a/src/Avalonia.Base/Styling/SelectorMatch.cs +++ b/src/Avalonia.Base/Styling/SelectorMatch.cs @@ -8,7 +8,7 @@ namespace Avalonia.Styling /// /// Describes how a matches a control and its type. /// - public enum SelectorMatchResult + internal enum SelectorMatchResult { /// /// The selector never matches this type. @@ -43,7 +43,7 @@ namespace Avalonia.Styling /// A selector match describes whether and how a matches a control, and /// in addition whether the selector can ever match a control of the same type. /// - public readonly record struct SelectorMatch + internal readonly record struct SelectorMatch { /// /// A selector match with the result of . diff --git a/src/Avalonia.Base/Styling/TemplateSelector.cs b/src/Avalonia.Base/Styling/TemplateSelector.cs index b253efc6d2..1fa2ca2d0f 100644 --- a/src/Avalonia.Base/Styling/TemplateSelector.cs +++ b/src/Avalonia.Base/Styling/TemplateSelector.cs @@ -18,13 +18,13 @@ namespace Avalonia.Styling } /// - public override bool InTemplate => true; + internal override bool InTemplate => true; /// - public override bool IsCombinator => true; + internal override bool IsCombinator => true; /// - public override Type? TargetType => null; + internal override Type? TargetType => null; public override string ToString(Style? owner) { @@ -36,7 +36,7 @@ namespace Avalonia.Styling return _selectorString; } - protected override SelectorMatch Evaluate(StyledElement control, IStyle? parent, bool subscribe) + private protected override SelectorMatch Evaluate(StyledElement control, IStyle? parent, bool subscribe) { var templatedParent = control.TemplatedParent as StyledElement; @@ -48,7 +48,7 @@ namespace Avalonia.Styling return _parent.Match(templatedParent, parent, subscribe); } - protected override Selector? MovePrevious() => null; - protected override Selector? MovePreviousOrParent() => _parent; + private protected override Selector? MovePrevious() => null; + private protected override Selector? MovePreviousOrParent() => _parent; } } diff --git a/src/Avalonia.Base/Styling/TypeNameAndClassSelector.cs b/src/Avalonia.Base/Styling/TypeNameAndClassSelector.cs index 2bd05242f5..81b204761b 100644 --- a/src/Avalonia.Base/Styling/TypeNameAndClassSelector.cs +++ b/src/Avalonia.Base/Styling/TypeNameAndClassSelector.cs @@ -58,7 +58,7 @@ namespace Avalonia.Styling } /// - public override bool InTemplate => _previous?.InTemplate ?? false; + internal override bool InTemplate => _previous?.InTemplate ?? false; /// /// Gets the name of the control to match. @@ -66,10 +66,10 @@ namespace Avalonia.Styling public string? Name { get; set; } /// - public override Type? TargetType => _targetType ?? _previous?.TargetType; + internal override Type? TargetType => _targetType ?? _previous?.TargetType; /// - public override bool IsCombinator => false; + internal override bool IsCombinator => false; /// /// Whether the selector matches the concrete or any object which @@ -89,7 +89,7 @@ namespace Avalonia.Styling } /// - protected override SelectorMatch Evaluate(StyledElement control, IStyle? parent, bool subscribe) + private protected override SelectorMatch Evaluate(StyledElement control, IStyle? parent, bool subscribe) { if (TargetType != null) { @@ -134,8 +134,8 @@ namespace Avalonia.Styling return Name == null ? SelectorMatch.AlwaysThisType : SelectorMatch.AlwaysThisInstance; } - protected override Selector? MovePrevious() => _previous; - protected override Selector? MovePreviousOrParent() => _previous; + private protected override Selector? MovePrevious() => _previous; + private protected override Selector? MovePreviousOrParent() => _previous; private string BuildSelectorString(Style? owner) { diff --git a/tests/Avalonia.Benchmarks/Styling/SelectorBenchmark.cs b/tests/Avalonia.Benchmarks/Styling/SelectorBenchmark.cs index 11bc5ce35f..e4799d46b8 100644 --- a/tests/Avalonia.Benchmarks/Styling/SelectorBenchmark.cs +++ b/tests/Avalonia.Benchmarks/Styling/SelectorBenchmark.cs @@ -37,62 +37,62 @@ namespace Avalonia.Benchmarks.Styling } [Benchmark] - public SelectorMatch IsSelector_NoMatch() + public void IsSelector_NoMatch() { - return _isCalendarSelector.Match(_notMatchingControl); + _isCalendarSelector.Match(_notMatchingControl); } [Benchmark] - public SelectorMatch IsSelector_Match() + public void IsSelector_Match() { - return _isCalendarSelector.Match(_matchingControl); + _isCalendarSelector.Match(_matchingControl); } [Benchmark] - public SelectorMatch ClassSelector_NoMatch() + public void ClassSelector_NoMatch() { - return _classSelector.Match(_notMatchingControl); + _classSelector.Match(_notMatchingControl); } [Benchmark] - public SelectorMatch ClassSelector_Match() + public void ClassSelector_Match() { - return _classSelector.Match(_matchingControl); + _classSelector.Match(_matchingControl); } [Benchmark] - public SelectorMatch OrSelector_One_Match() + public void OrSelector_One_Match() { - return _orSelectorTwo.Match(_matchingControl); + _orSelectorTwo.Match(_matchingControl); } [Benchmark] - public SelectorMatch OrSelector_Five_Match() + public void OrSelector_Five_Match() { - return _orSelectorFive.Match(_matchingControl); + _orSelectorFive.Match(_matchingControl); } } internal class AlwaysMatchSelector : Selector { - public override bool InTemplate => false; + internal override bool InTemplate => false; - public override bool IsCombinator => false; + internal override bool IsCombinator => false; - public override Type TargetType => null; + internal override Type TargetType => null; public override string ToString(Style owner) { return "Always"; } - protected override SelectorMatch Evaluate(StyledElement control, IStyle parent, bool subscribe) + private protected override SelectorMatch Evaluate(StyledElement control, IStyle parent, bool subscribe) { return SelectorMatch.AlwaysThisType; } - protected override Selector MovePrevious() => null; + private protected override Selector MovePrevious() => null; - protected override Selector MovePreviousOrParent() => null; + private protected override Selector MovePreviousOrParent() => null; } } diff --git a/tests/Avalonia.UnitTests/StyleHelpers.cs b/tests/Avalonia.UnitTests/StyleHelpers.cs index 00f74769d3..ed95519830 100644 --- a/tests/Avalonia.UnitTests/StyleHelpers.cs +++ b/tests/Avalonia.UnitTests/StyleHelpers.cs @@ -6,9 +6,9 @@ namespace Avalonia.UnitTests { public static class StyleHelpers { - public static SelectorMatchResult TryAttach(Style style, StyledElement element, object? host = null) + public static void TryAttach(Style style, StyledElement element, object? host = null) { - return style.TryAttach(element, host ?? element, PropertyStore.FrameType.Style); + style.TryAttach(element, host ?? element, PropertyStore.FrameType.Style); } } } From 34a83e26d14abf793dedc2b7f1d773847de73e90 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 18 May 2023 14:39:58 +0200 Subject: [PATCH 4/5] Renamed ISetter as SetterBase. Allows us to hide the mechanics of initializing a setter. --- src/Avalonia.Base/Styling/ISetter.cs | 24 ------------------- src/Avalonia.Base/Styling/ISetterInstance.cs | 2 +- src/Avalonia.Base/Styling/ISetterValue.cs | 4 ++-- src/Avalonia.Base/Styling/Setter.cs | 4 ++-- src/Avalonia.Base/Styling/SetterBase.cs | 12 ++++++++++ src/Avalonia.Base/Styling/StyleBase.cs | 6 ++--- src/Avalonia.Controls/ContextMenu.cs | 2 +- src/Avalonia.Controls/Control.cs | 2 +- .../AvaloniaXamlIlBindingPathTransformer.cs | 2 +- .../AvaloniaXamlIlWellKnownTypes.cs | 4 ++-- .../Avalonia.Markup/Data/TemplateBinding.cs | 2 +- 11 files changed, 26 insertions(+), 38 deletions(-) delete mode 100644 src/Avalonia.Base/Styling/ISetter.cs create mode 100644 src/Avalonia.Base/Styling/SetterBase.cs diff --git a/src/Avalonia.Base/Styling/ISetter.cs b/src/Avalonia.Base/Styling/ISetter.cs deleted file mode 100644 index 22af90b446..0000000000 --- a/src/Avalonia.Base/Styling/ISetter.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using Avalonia.Metadata; - -namespace Avalonia.Styling -{ - /// - /// Represents a setter for a . - /// - [NotClientImplementable] - public interface ISetter - { - /// - /// Instances a setter on a control. - /// - /// The style which contains the setter. - /// The control. - /// An . - /// - /// This method should return an which can be used to apply - /// the setter to the specified control. - /// - ISetterInstance Instance(IStyleInstance styleInstance, StyledElement target); - } -} diff --git a/src/Avalonia.Base/Styling/ISetterInstance.cs b/src/Avalonia.Base/Styling/ISetterInstance.cs index 4a65d6deeb..f8a7e7d346 100644 --- a/src/Avalonia.Base/Styling/ISetterInstance.cs +++ b/src/Avalonia.Base/Styling/ISetterInstance.cs @@ -3,7 +3,7 @@ namespace Avalonia.Styling { /// - /// Represents an that has been instanced on a control. + /// Represents a that has been instanced on a control. /// [Unstable] public interface ISetterInstance diff --git a/src/Avalonia.Base/Styling/ISetterValue.cs b/src/Avalonia.Base/Styling/ISetterValue.cs index 0fd245a429..800d8275b5 100644 --- a/src/Avalonia.Base/Styling/ISetterValue.cs +++ b/src/Avalonia.Base/Styling/ISetterValue.cs @@ -3,13 +3,13 @@ namespace Avalonia.Styling { /// - /// Customizes the behavior of a class when added as a value to an . + /// Customizes the behavior of a class when added as a value to a . /// public interface ISetterValue { /// /// Notifies that the object has been added as a setter value. /// - void Initialize(ISetter setter); + void Initialize(SetterBase setter); } } diff --git a/src/Avalonia.Base/Styling/Setter.cs b/src/Avalonia.Base/Styling/Setter.cs index 9b009be6d2..e5b2bed738 100644 --- a/src/Avalonia.Base/Styling/Setter.cs +++ b/src/Avalonia.Base/Styling/Setter.cs @@ -14,7 +14,7 @@ namespace Avalonia.Styling /// A is used to set a value on a /// depending on a condition. /// - public class Setter : ISetter, IValueEntry, ISetterInstance, IAnimationSetter + public class Setter : SetterBase, IValueEntry, ISetterInstance, IAnimationSetter { private object? _value; private DirectPropertySetterInstance? _direct; @@ -66,7 +66,7 @@ namespace Avalonia.Styling void IValueEntry.Unsubscribe() { } [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = TrimmingMessages.ImplicitTypeConvertionSupressWarningMessage)] - ISetterInstance ISetter.Instance(IStyleInstance instance, StyledElement target) + internal override ISetterInstance Instance(IStyleInstance instance, StyledElement target) { if (target is not AvaloniaObject ao) throw new InvalidOperationException("Don't know how to instance a style on this type."); diff --git a/src/Avalonia.Base/Styling/SetterBase.cs b/src/Avalonia.Base/Styling/SetterBase.cs new file mode 100644 index 0000000000..24cd525130 --- /dev/null +++ b/src/Avalonia.Base/Styling/SetterBase.cs @@ -0,0 +1,12 @@ +namespace Avalonia.Styling +{ + /// + /// Represents the base class for value setters. + /// + public abstract class SetterBase + { + internal abstract ISetterInstance Instance( + IStyleInstance styleInstance, + StyledElement target); + } +} diff --git a/src/Avalonia.Base/Styling/StyleBase.cs b/src/Avalonia.Base/Styling/StyleBase.cs index 7dfa516bce..318e8d6890 100644 --- a/src/Avalonia.Base/Styling/StyleBase.cs +++ b/src/Avalonia.Base/Styling/StyleBase.cs @@ -16,7 +16,7 @@ namespace Avalonia.Styling private IResourceHost? _owner; private StyleChildren? _children; private IResourceDictionary? _resources; - private List? _setters; + private List? _setters; private List? _animations; private StyleInstance? _sharedInstance; @@ -60,7 +60,7 @@ namespace Avalonia.Styling } } - public IList Setters => _setters ??= new List(); + public IList Setters => _setters ??= new(); public IList Animations => _animations ??= new List(); bool IResourceNode.HasResources => _resources?.Count > 0; @@ -69,7 +69,7 @@ namespace Avalonia.Styling internal bool HasChildren => _children?.Count > 0; internal bool HasSettersOrAnimations => _setters?.Count > 0 || _animations?.Count > 0; - public void Add(ISetter setter) => Setters.Add(setter); + public void Add(SetterBase setter) => Setters.Add(setter); public void Add(IStyle style) => Children.Add(style); public event EventHandler? OwnerChanged; diff --git a/src/Avalonia.Controls/ContextMenu.cs b/src/Avalonia.Controls/ContextMenu.cs index 97a8c6fe97..39a98bd48a 100644 --- a/src/Avalonia.Controls/ContextMenu.cs +++ b/src/Avalonia.Controls/ContextMenu.cs @@ -285,7 +285,7 @@ namespace Avalonia.Controls } } - void ISetterValue.Initialize(ISetter setter) + void ISetterValue.Initialize(SetterBase setter) { // ContextMenu can be assigned to the ContextMenu property in a setter. This overrides // the behavior defined in Control which requires controls to be wrapped in a