diff --git a/src/Controls/src/Core/View/View.cs b/src/Controls/src/Core/View/View.cs index 3d8d52d22de4..48a88634bf16 100644 --- a/src/Controls/src/Core/View/View.cs +++ b/src/Controls/src/Core/View/View.cs @@ -1,10 +1,10 @@ #nullable disable using System; +using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Collections.Specialized; using System.Linq; -using Microsoft.Maui; using Microsoft.Maui.Controls.Internals; using Microsoft.Maui.Controls.Platform; using Microsoft.Maui.Graphics; @@ -96,19 +96,20 @@ protected internal View() _gestureManager = new GestureManager(this); _gestureRecognizers.CollectionChanged += (sender, args) => { - void AddItems(IEnumerable elements) + void AddItems(IList newItems) { - foreach (IElementDefinition item in elements) + foreach (IElementDefinition item in newItems) { - ValidateGesture(item as IGestureRecognizer); + var gestureRecognizer = item as IGestureRecognizer; + ValidateGesture(gestureRecognizer); item.Parent = this; - GestureController.CompositeGestureRecognizers.Add(item as IGestureRecognizer); + GestureController.CompositeGestureRecognizers.Add(gestureRecognizer); } } - void RemoveItems(IEnumerable elements) + void RemoveItems(IList oldItems) { - foreach (IElementDefinition item in elements) + foreach (IElementDefinition item in oldItems) { item.Parent = null; GestureController.CompositeGestureRecognizers.Remove(item as IGestureRecognizer); @@ -118,41 +119,46 @@ void RemoveItems(IEnumerable elements) switch (args.Action) { case NotifyCollectionChangedAction.Add: - AddItems(args.NewItems.OfType()); + AddItems(args.NewItems); break; case NotifyCollectionChangedAction.Remove: - RemoveItems(args.OldItems.OfType()); + RemoveItems(args.OldItems); break; case NotifyCollectionChangedAction.Replace: - AddItems(args.NewItems.OfType()); - RemoveItems(args.OldItems.OfType()); + AddItems(args.NewItems); + RemoveItems(args.OldItems); break; case NotifyCollectionChangedAction.Reset: - List remove = new List(); - List add = new List(); - - foreach (IElementDefinition item in _gestureRecognizers.OfType()) + foreach (IGestureRecognizer gestureRecognizer in _gestureRecognizers) { - if (!_gestureRecognizers.Contains((IGestureRecognizer)item)) - add.Add(item); - item.Parent = this; + if (gestureRecognizer is IElementDefinition item) + { + item.Parent = this; + } } - foreach (IElementDefinition item in GestureController.CompositeGestureRecognizers.OfType()) - { - if (item == _recognizerForPointerOverState) - continue; + HashSet compositeGestureRecognizers = new(GestureController.CompositeGestureRecognizers); - if (_gestureRecognizers.Contains((IGestureRecognizer)item)) - item.Parent = this; - else - remove.Add(item); + foreach (IGestureRecognizer gestureRecognizer in compositeGestureRecognizers) + { + if (gestureRecognizer is IElementDefinition item) + { + if (item == _recognizerForPointerOverState) + continue; + + if (_gestureRecognizers.Contains(gestureRecognizer)) + { + item.Parent = this; + } + else + { + item.Parent = null; + GestureController.CompositeGestureRecognizers.Remove(gestureRecognizer); + } + } } - AddItems(add); - RemoveItems(remove); - break; } }; diff --git a/src/Core/tests/Benchmarks/Benchmarks/GestureRecognizerBenchmarker.cs b/src/Core/tests/Benchmarks/Benchmarks/GestureRecognizerBenchmarker.cs new file mode 100644 index 000000000000..b433675048e9 --- /dev/null +++ b/src/Core/tests/Benchmarks/Benchmarks/GestureRecognizerBenchmarker.cs @@ -0,0 +1,123 @@ +using BenchmarkDotNet.Attributes; +using Microsoft.Maui.Controls; + +namespace Microsoft.Maui.Handlers.Benchmarks; + +[MemoryDiagnoser] +public class GestureRecognizerBenchmarker +{ + View[] _views = null; + IGestureRecognizer[] _gestureRecognizers = null; + + [GlobalSetup] + public void Setup() + { + _views = [ + new Border(), new BoxView(), new CarouselView(), new Grid(), new Entry(), new Picker(), new CollectionView(), + new CheckBox(), new DatePicker(), new Stepper(), new Slider(), new ActivityIndicator(), new Frame(), + new ContentView(), new ProgressBar(), new SearchBar(), new Switch(), new TimePicker(), new WebView(), new Button(), + ]; + + _gestureRecognizers = [ + new PointerGestureRecognizer(), new TapGestureRecognizer(), new PanGestureRecognizer(), + new SwipeGestureRecognizer(), new DragGestureRecognizer(), new DropGestureRecognizer(), + ]; + } + + [Benchmark] + public void AddLotsOfGestureRecognizers() + { + var layout = new VerticalStackLayout(); + + AddGestureRecognizers(layout, _gestureRecognizers); + + for (int i = 0; i < 100; i++) + { + var childLayout = new VerticalStackLayout(); + + AddGestureRecognizers(childLayout, _gestureRecognizers); + + foreach (var view in _views) + { + AddGestureRecognizers(view, _gestureRecognizers); + + childLayout.Add(view); + } + + layout.Add(childLayout); + } + } + + + [Benchmark] + public void ClearLotsOfGestureRecognizers() + { + var layout = new VerticalStackLayout(); + + for (int i = 0; i < 1000; i++) + { + AddGestureRecognizers(layout, _gestureRecognizers); + } + + layout.GestureRecognizers.Clear(); + } + + + [Benchmark] + public void RemoveLotsOfGestureRecognizers() + { + var layout = new VerticalStackLayout(); + + foreach (var gestureRecognizer in _gestureRecognizers) + { + for (int i = 0; i < 1000; i++) + { + layout.GestureRecognizers.Remove(gestureRecognizer); + } + } + } + + [Benchmark] + public void AddOneGestureRecognizer() + { + var gestureRecognizer = new PointerGestureRecognizer(); + + for (int i = 0; i < 1000; i++) + { + var button = new Button(); + button.GestureRecognizers.Add(gestureRecognizer); + } + } + + [Benchmark] + public void RemoveOneGestureRecognizer() + { + var gestureRecognizer = new PointerGestureRecognizer(); + + for (int i = 0; i < 1000; i++) + { + var button = new Button(); + button.GestureRecognizers.Remove(gestureRecognizer); + } + } + + [Benchmark] + public void ClearOneGestureRecognizer() + { + for (int i = 0; i < 1000; i++) + { + var button = new Button(); + var gestureRecognizer = new PointerGestureRecognizer(); + button.GestureRecognizers.Add(gestureRecognizer); + button.GestureRecognizers.Clear(); + } + } + + private static void AddGestureRecognizers(View view, params IGestureRecognizer[] gestureRecognizers) + { + foreach (var gestureRecognizer in gestureRecognizers) + { + view.GestureRecognizers.Add(gestureRecognizer); + } + } +}