From 2856c84fe3d71d4de241aadb360ac367339236f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Ku=C4=8Dera?= <10546952+miloush@users.noreply.github.com> Date: Wed, 11 Feb 2026 11:03:49 +0000 Subject: [PATCH] FindAncestorOfType and FindDescendantOfType with predicates (#20661) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Find ancestors/descendants with a predicate * Use Predicate instead of Func * Find ancestors/descendants with a predicate * Use Predicate instead of Func * Predicate to Predicate --------- Co-authored-by: Jan Kučera --- .../VisualTree/VisualExtensions.cs | 44 ++++++++++++++++--- .../VisualExtensionsTests.cs | 43 ++++++++++++++++++ 2 files changed, 81 insertions(+), 6 deletions(-) diff --git a/src/Avalonia.Base/VisualTree/VisualExtensions.cs b/src/Avalonia.Base/VisualTree/VisualExtensions.cs index 670d879f29..bb9e5d4d0e 100644 --- a/src/Avalonia.Base/VisualTree/VisualExtensions.cs +++ b/src/Avalonia.Base/VisualTree/VisualExtensions.cs @@ -147,6 +147,19 @@ namespace Avalonia.VisualTree /// If given visual should be included in search. /// First ancestor of given type. public static T? FindAncestorOfType(this Visual? visual, bool includeSelf = false) where T : class + { + return FindAncestorOfType(visual, includeSelf, predicate: null); + } + + /// + /// Finds first ancestor of given type that matches a predicate. + /// + /// Ancestor type. + /// The visual. + /// If given visual should be included in search. + /// The predicate that the ancestor must match. + /// First ancestor of given type. + public static T? FindAncestorOfType(this Visual? visual, bool includeSelf, Predicate? predicate) where T : class { if (visual is null) { @@ -159,7 +172,10 @@ namespace Avalonia.VisualTree { if (parent is T result) { - return result; + if (predicate == null || predicate(result)) + { + return result; + } } parent = parent.VisualParent; @@ -176,18 +192,31 @@ namespace Avalonia.VisualTree /// If given visual should be included in search. /// First descendant of given type. public static T? FindDescendantOfType(this Visual? visual, bool includeSelf = false) where T : class + { + return FindDescendantOfType(visual, includeSelf, predicate: null); + } + + /// + /// Finds first descendant of given type that matches given predicate. + /// + /// Descendant type. + /// The visual. + /// If given visual should be included in search. + /// The predicate that the descendant must match. + /// First descendant of given type that matches given predicate. + public static T? FindDescendantOfType(this Visual? visual, bool includeSelf, Predicate? predicate) where T : class { if (visual is null) { return null; } - if (includeSelf && visual is T result) + if (includeSelf && visual is T result && (predicate == null || predicate(result))) { return result; } - return FindDescendantOfTypeCore(visual); + return FindDescendantOfTypeCore(visual, predicate); } /// @@ -492,7 +521,7 @@ namespace Avalonia.VisualTree .Select(x => x.Element!); } - private static T? FindDescendantOfTypeCore(Visual visual) where T : class + private static T? FindDescendantOfTypeCore(Visual visual, Predicate? predicate) where T : class { var visualChildren = visual.VisualChildren; var visualChildrenCount = visualChildren.Count; @@ -503,10 +532,13 @@ namespace Avalonia.VisualTree if (child is T result) { - return result; + if (predicate == null || predicate(result)) + { + return result; + } } - var childResult = FindDescendantOfTypeCore(child); + var childResult = FindDescendantOfTypeCore(child, predicate); if (!(childResult is null)) { diff --git a/tests/Avalonia.Base.UnitTests/VisualExtensionsTests.cs b/tests/Avalonia.Base.UnitTests/VisualExtensionsTests.cs index 4863c0fa61..270702f178 100644 --- a/tests/Avalonia.Base.UnitTests/VisualExtensionsTests.cs +++ b/tests/Avalonia.Base.UnitTests/VisualExtensionsTests.cs @@ -22,6 +22,23 @@ namespace Avalonia.Base.UnitTests Assert.Equal(root, target.FindAncestorOfType()); } + [Fact] + public void FindAncestorOfType_Finds_Visible_Parent() + { + StackPanel target; + + var root = new TestRoot + { + Child = new TestRoot + { + Child = target = new StackPanel(), + IsVisible = false + } + }; + + Assert.Equal(root, target.FindAncestorOfType(false, v => v.IsVisible)); + } + [Fact] public void FindAncestorOfType_Finds_Ancestor_Of_Nested_Child() { @@ -85,6 +102,32 @@ namespace Avalonia.Base.UnitTests Assert.Equal(target, root.FindDescendantOfType