diff --git a/src/Avalonia.Base/VisualTree/VisualExtensions.cs b/src/Avalonia.Base/VisualTree/VisualExtensions.cs index 55176a6502..b202a2e4b7 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); } /// @@ -485,7 +514,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; @@ -496,10 +525,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