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