diff --git a/src/Avalonia.Styling/Styling/TypeNameAndClassSelector.cs b/src/Avalonia.Styling/Styling/TypeNameAndClassSelector.cs index 362ac86e50..f1fd2f6c7f 100644 --- a/src/Avalonia.Styling/Styling/TypeNameAndClassSelector.cs +++ b/src/Avalonia.Styling/Styling/TypeNameAndClassSelector.cs @@ -18,8 +18,9 @@ namespace Avalonia.Styling internal class TypeNameAndClassSelector : Selector { private readonly Selector _previous; + private readonly Lazy> _classes = new Lazy>(() => new List()); private Type _targetType; - private Lazy> _classes = new Lazy>(() => new List()); + private string _selectorString; public static TypeNameAndClassSelector OfType(Selector previous, Type targetType) @@ -27,6 +28,7 @@ namespace Avalonia.Styling var result = new TypeNameAndClassSelector(previous); result._targetType = targetType; result.IsConcreteType = true; + return result; } @@ -35,6 +37,7 @@ namespace Avalonia.Styling var result = new TypeNameAndClassSelector(previous); result._targetType = targetType; result.IsConcreteType = false; + return result; } @@ -42,6 +45,7 @@ namespace Avalonia.Styling { var result = new TypeNameAndClassSelector(previous); result.Name = name; + return result; } @@ -49,6 +53,7 @@ namespace Avalonia.Styling { var result = new TypeNameAndClassSelector(previous); result.Classes.Add(className); + return result; } @@ -126,9 +131,11 @@ namespace Avalonia.Styling if (subscribe) { var observable = new ClassObserver(control.Classes, _classes.Value); + return new SelectorMatch(observable); } - else if (!Matches(control.Classes)) + + if (!AreClassesMatching(control.Classes, Classes)) { return SelectorMatch.NeverThisInstance; } @@ -139,21 +146,6 @@ namespace Avalonia.Styling protected override Selector MovePrevious() => _previous; - private bool Matches(IEnumerable classes) - { - int remaining = Classes.Count; - - foreach (var c in classes) - { - if (Classes.Contains(c)) - { - --remaining; - } - } - - return remaining == 0; - } - private string BuildSelectorString() { var builder = new StringBuilder(); @@ -199,11 +191,41 @@ namespace Avalonia.Styling return builder.ToString(); } - private class ClassObserver : LightweightObservableBase + private static bool AreClassesMatching(IReadOnlyList classes, IList toMatch) { - readonly IList _match; - IAvaloniaReadOnlyList _classes; - bool _value; + int remainingMatches = toMatch.Count; + int classesCount = classes.Count; + + // Early bail out - we can't match if control does not have enough classes. + if (classesCount < remainingMatches) + { + return false; + } + + for (var i = 0; i < classesCount; i++) + { + var c = classes[i]; + + if (toMatch.Contains(c)) + { + --remainingMatches; + + // Already matched so we can skip checking other classes. + if (remainingMatches == 0) + { + break; + } + } + } + + return remainingMatches == 0; + } + + private sealed class ClassObserver : LightweightObservableBase + { + private readonly IList _match; + private readonly IAvaloniaReadOnlyList _classes; + private bool _hasMatch; public ClassObserver(IAvaloniaReadOnlyList classes, IList match) { @@ -215,42 +237,32 @@ namespace Avalonia.Styling protected override void Initialize() { - _value = GetResult(); + _hasMatch = IsMatching(); _classes.CollectionChanged += ClassesChanged; } protected override void Subscribed(IObserver observer, bool first) { - observer.OnNext(_value); + observer.OnNext(_hasMatch); } private void ClassesChanged(object sender, NotifyCollectionChangedEventArgs e) { if (e.Action != NotifyCollectionChangedAction.Move) { - var value = GetResult(); + var hasMatch = IsMatching(); - if (value != _value) + if (hasMatch != _hasMatch) { - PublishNext(GetResult()); - _value = value; + PublishNext(hasMatch); + _hasMatch = hasMatch; } } } - private bool GetResult() + private bool IsMatching() { - int remaining = _match.Count; - - foreach (var c in _classes) - { - if (_match.Contains(c)) - { - --remaining; - } - } - - return remaining == 0; + return AreClassesMatching(_classes, _match); } } }