diff --git a/src/Core/src/Core/Extensions/VisualTreeElementExtensions.cs b/src/Core/src/Core/Extensions/VisualTreeElementExtensions.cs index dcd56aa2d1cb..be8875d010d8 100644 --- a/src/Core/src/Core/Extensions/VisualTreeElementExtensions.cs +++ b/src/Core/src/Core/Extensions/VisualTreeElementExtensions.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using Microsoft.Maui.Graphics; @@ -145,14 +145,27 @@ static List GetVisualTreeElementsWindowsInternal(IVisualTree if (uiElement != null) { - var uniqueElements = findChildren(uiElement).Distinct(); - var viewTree = visualElement.GetVisualTreeDescendants().Where(n => n is IView view && view.Handler is not null).Select(n => new Tuple((IView)n, ((IView)n).ToPlatform())); - var testList = viewTree.Where(n => uniqueElements.Contains(n.Item2)).Select(n => n.Item1); - if (testList != null && testList.Any()) - visualElements.AddRange(testList.Select(n => (IVisualTreeElement)n)); + var uniqueElements = findChildren(uiElement).ToHashSet(); + + var descendants = visualElement.GetVisualTreeDescendants(); + + // Add in reverse order + for (int i = descendants.Count - 1; i >= 0; i--) + { + var descendant = descendants[i]; + + if (descendant is not IView view || view.Handler is null) + { + continue; + } + + if (uniqueElements.Contains(view.ToPlatform())) + { + visualElements.Add(descendant); + } + } } - visualElements.Reverse(); return visualElements; } #endif diff --git a/src/Core/tests/Benchmarks/Benchmarks/VisualTreeBenchmarker.cs b/src/Core/tests/Benchmarks/Benchmarks/VisualTreeBenchmarker.cs new file mode 100644 index 000000000000..b38b9a09e0e4 --- /dev/null +++ b/src/Core/tests/Benchmarks/Benchmarks/VisualTreeBenchmarker.cs @@ -0,0 +1,49 @@ +using BenchmarkDotNet.Attributes; +using Microsoft.Maui.Controls; + +namespace Microsoft.Maui.Benchmarks +{ + [MemoryDiagnoser] + public class VisualTreeBenchmarker + { + static readonly View[] 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(), + ]; + + private const int Iterations = 100; + + [Benchmark] + public void GetVisualTreeElements() + { + var layout = new VerticalStackLayout(); + + for (int i = 0; i < Iterations; i++) + { + var childLayout = new VerticalStackLayout(); + + foreach (var view in Views) + { + childLayout.Add(view); + + var grandchildLayout = new VerticalStackLayout(); + + foreach (var view2 in Views) + { + grandchildLayout.Add(view); + grandchildLayout.GetVisualTreeElements(grandchildLayout.Frame); + } + + layout.Add(grandchildLayout); + + childLayout.GetVisualTreeElements(childLayout.Frame); + } + + layout.Add(childLayout); + + layout.GetVisualTreeElements(layout.Frame); + } + } + } +}