Browse Source

FindAncestorOfType and FindDescendantOfType with predicates (#20661)

* Find ancestors/descendants with a predicate

* Use Predicate instead of Func

* Find ancestors/descendants with a predicate

* Use Predicate instead of Func

* Predicate<Visual> to Predicate<T>

---------

Co-authored-by: Jan Kučera <miloush@users.noreply.github.com>
release/latest
Jan Kučera 1 month ago
committed by Julien Lebosquain
parent
commit
2856c84fe3
No known key found for this signature in database GPG Key ID: 1833CAD10ACC46FD
  1. 40
      src/Avalonia.Base/VisualTree/VisualExtensions.cs
  2. 43
      tests/Avalonia.Base.UnitTests/VisualExtensionsTests.cs

40
src/Avalonia.Base/VisualTree/VisualExtensions.cs

@ -147,6 +147,19 @@ namespace Avalonia.VisualTree
/// <param name="includeSelf">If given visual should be included in search.</param>
/// <returns>First ancestor of given type.</returns>
public static T? FindAncestorOfType<T>(this Visual? visual, bool includeSelf = false) where T : class
{
return FindAncestorOfType<T>(visual, includeSelf, predicate: null);
}
/// <summary>
/// Finds first ancestor of given type that matches a predicate.
/// </summary>
/// <typeparam name="T">Ancestor type.</typeparam>
/// <param name="visual">The visual.</param>
/// <param name="includeSelf">If given visual should be included in search.</param>
/// <param name="predicate">The predicate that the ancestor must match.</param>
/// <returns>First ancestor of given type.</returns>
public static T? FindAncestorOfType<T>(this Visual? visual, bool includeSelf, Predicate<T>? predicate) where T : class
{
if (visual is null)
{
@ -158,9 +171,12 @@ namespace Avalonia.VisualTree
while (parent != null)
{
if (parent is T result)
{
if (predicate == null || predicate(result))
{
return result;
}
}
parent = parent.VisualParent;
}
@ -176,18 +192,31 @@ namespace Avalonia.VisualTree
/// <param name="includeSelf">If given visual should be included in search.</param>
/// <returns>First descendant of given type.</returns>
public static T? FindDescendantOfType<T>(this Visual? visual, bool includeSelf = false) where T : class
{
return FindDescendantOfType<T>(visual, includeSelf, predicate: null);
}
/// <summary>
/// Finds first descendant of given type that matches given predicate.
/// </summary>
/// <typeparam name="T">Descendant type.</typeparam>
/// <param name="visual">The visual.</param>
/// <param name="includeSelf">If given visual should be included in search.</param>
/// <param name="predicate">The predicate that the descendant must match.</param>
/// <returns>First descendant of given type that matches given predicate.</returns>
public static T? FindDescendantOfType<T>(this Visual? visual, bool includeSelf, Predicate<T>? 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<T>(visual);
return FindDescendantOfTypeCore<T>(visual, predicate);
}
/// <summary>
@ -492,7 +521,7 @@ namespace Avalonia.VisualTree
.Select(x => x.Element!);
}
private static T? FindDescendantOfTypeCore<T>(Visual visual) where T : class
private static T? FindDescendantOfTypeCore<T>(Visual visual, Predicate<T>? predicate) where T : class
{
var visualChildren = visual.VisualChildren;
var visualChildrenCount = visualChildren.Count;
@ -502,11 +531,14 @@ namespace Avalonia.VisualTree
Visual child = visualChildren[i];
if (child is T result)
{
if (predicate == null || predicate(result))
{
return result;
}
}
var childResult = FindDescendantOfTypeCore<T>(child);
var childResult = FindDescendantOfTypeCore<T>(child, predicate);
if (!(childResult is null))
{

43
tests/Avalonia.Base.UnitTests/VisualExtensionsTests.cs

@ -22,6 +22,23 @@ namespace Avalonia.Base.UnitTests
Assert.Equal(root, target.FindAncestorOfType<TestRoot>());
}
[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<TestRoot>(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<Button>());
}
[Fact]
public void FindDescendantOfType_Finds_Nested_Visible_Child()
{
Button target;
var root = new TestRoot
{
Child = new StackPanel
{
Children =
{
new StackPanel
{
Children =
{
new Button { IsVisible = false },
(target = new Button())
}
}
}
}
};
Assert.Equal(target, root.FindDescendantOfType<Button>(false, v => v.IsVisible));
}
[Fact]
public void FindCommonVisualAncestor_First_Is_Parent_Of_Second()
{

Loading…
Cancel
Save