diff --git a/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs b/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs
index 3715bc52a4..8ad622ba4a 100644
--- a/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs
+++ b/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs
@@ -392,7 +392,7 @@ namespace Avalonia.Controls.Platform
{
var control = e.Source as ILogical;
- if (!Menu.IsLogicalParentOf(control))
+ if (!Menu.IsLogicalAncestorOf(control))
{
Menu.Close();
}
diff --git a/src/Avalonia.Dialogs/ManagedFileChooser.xaml.cs b/src/Avalonia.Dialogs/ManagedFileChooser.xaml.cs
index b967b40c0d..616f260397 100644
--- a/src/Avalonia.Dialogs/ManagedFileChooser.xaml.cs
+++ b/src/Avalonia.Dialogs/ManagedFileChooser.xaml.cs
@@ -34,7 +34,7 @@ namespace Avalonia.Dialogs
return;
}
- var isQuickLink = _quickLinksRoot.IsLogicalParentOf(e.Source as Control);
+ var isQuickLink = _quickLinksRoot.IsLogicalAncestorOf(e.Source as Control);
if (e.ClickCount == 2 || isQuickLink)
{
if (model.ItemType == ManagedFileChooserItemType.File)
diff --git a/src/Avalonia.Styling/LogicalTree/LogicalExtensions.cs b/src/Avalonia.Styling/LogicalTree/LogicalExtensions.cs
index 1003375978..458ab0fce2 100644
--- a/src/Avalonia.Styling/LogicalTree/LogicalExtensions.cs
+++ b/src/Avalonia.Styling/LogicalTree/LogicalExtensions.cs
@@ -1,11 +1,18 @@
using System;
using System.Collections.Generic;
-using System.Linq;
namespace Avalonia.LogicalTree
{
+ ///
+ /// Provides extension methods for working with the logical tree.
+ ///
public static class LogicalExtensions
{
+ ///
+ /// Enumerates the ancestors of an in the logical tree.
+ ///
+ /// The logical.
+ /// The logical's ancestors.
public static IEnumerable GetLogicalAncestors(this ILogical logical)
{
Contract.Requires(logical != null);
@@ -19,6 +26,11 @@ namespace Avalonia.LogicalTree
}
}
+ ///
+ /// Enumerates an and its ancestors in the logical tree.
+ ///
+ /// The logical.
+ /// The logical and its ancestors.
public static IEnumerable GetSelfAndLogicalAncestors(this ILogical logical)
{
yield return logical;
@@ -29,11 +41,50 @@ namespace Avalonia.LogicalTree
}
}
+ ///
+ /// Finds first ancestor of given type.
+ ///
+ /// Ancestor type.
+ /// The logical.
+ /// If given logical should be included in search.
+ /// First ancestor of given type.
+ public static T FindLogicalAncestorOfType(this ILogical logical, bool includeSelf = false) where T : class
+ {
+ if (logical is null)
+ {
+ return null;
+ }
+
+ ILogical parent = includeSelf ? logical : logical.LogicalParent;
+
+ while (parent != null)
+ {
+ if (parent is T result)
+ {
+ return result;
+ }
+
+ parent = parent.LogicalParent;
+ }
+
+ return null;
+ }
+
+ ///
+ /// Enumerates the children of an in the logical tree.
+ ///
+ /// The logical.
+ /// The logical children.
public static IEnumerable GetLogicalChildren(this ILogical logical)
{
return logical.LogicalChildren;
}
+ ///
+ /// Enumerates the descendants of an in the logical tree.
+ ///
+ /// The logical.
+ /// The logical's ancestors.
public static IEnumerable GetLogicalDescendants(this ILogical logical)
{
foreach (ILogical child in logical.LogicalChildren)
@@ -47,6 +98,11 @@ namespace Avalonia.LogicalTree
}
}
+ ///
+ /// Enumerates an and its descendants in the logical tree.
+ ///
+ /// The logical.
+ /// The logical and its ancestors.
public static IEnumerable GetSelfAndLogicalDescendants(this ILogical logical)
{
yield return logical;
@@ -57,16 +113,56 @@ namespace Avalonia.LogicalTree
}
}
+ ///
+ /// Finds first descendant of given type.
+ ///
+ /// Descendant type.
+ /// The logical.
+ /// If given logical should be included in search.
+ /// First descendant of given type.
+ public static T FindLogicalDescendantOfType(this ILogical logical, bool includeSelf = false) where T : class
+ {
+ if (logical is null)
+ {
+ return null;
+ }
+
+ if (includeSelf && logical is T result)
+ {
+ return result;
+ }
+
+ return FindDescendantOfTypeCore(logical);
+ }
+
+ ///
+ /// Gets the logical parent of an .
+ ///
+ /// The logical.
+ /// The parent, or null if the logical is unparented.
public static ILogical GetLogicalParent(this ILogical logical)
{
return logical.LogicalParent;
}
+ ///
+ /// Gets the logical parent of an .
+ ///
+ /// The type of the logical parent.
+ /// The logical.
+ ///
+ /// The parent, or null if the logical is unparented or its parent is not of type .
+ ///
public static T GetLogicalParent(this ILogical logical) where T : class
{
return logical.LogicalParent as T;
}
+ ///
+ /// Enumerates the siblings of an in the logical tree.
+ ///
+ /// The logical.
+ /// The logical siblings.
public static IEnumerable GetLogicalSiblings(this ILogical logical)
{
ILogical parent = logical.LogicalParent;
@@ -80,9 +176,55 @@ namespace Avalonia.LogicalTree
}
}
- public static bool IsLogicalParentOf(this ILogical logical, ILogical target)
+ ///
+ /// Tests whether an is an ancestor of another logical.
+ ///
+ /// The logical.
+ /// The potential descendant.
+ ///
+ /// True if is an ancestor of ;
+ /// otherwise false.
+ ///
+ public static bool IsLogicalAncestorOf(this ILogical logical, ILogical target)
{
- return target.GetLogicalAncestors().Any(x => x == logical);
+ ILogical current = target?.LogicalParent;
+
+ while (current != null)
+ {
+ if (current == logical)
+ {
+ return true;
+ }
+
+ current = current.LogicalParent;
+ }
+
+ return false;
+ }
+
+ private static T FindDescendantOfTypeCore(ILogical logical) where T : class
+ {
+ var logicalChildren = logical.LogicalChildren;
+ var logicalChildrenCount = logicalChildren.Count;
+
+ for (var i = 0; i < logicalChildrenCount; i++)
+ {
+ ILogical child = logicalChildren[i];
+
+ if (child is T result)
+ {
+ return result;
+ }
+
+ var childResult = FindDescendantOfTypeCore(child);
+
+ if (!(childResult is null))
+ {
+ return childResult;
+ }
+ }
+
+ return null;
}
}
}
diff --git a/src/Avalonia.Visuals/VisualTree/VisualExtensions.cs b/src/Avalonia.Visuals/VisualTree/VisualExtensions.cs
index 4b3e757e7a..8c4004efdc 100644
--- a/src/Avalonia.Visuals/VisualTree/VisualExtensions.cs
+++ b/src/Avalonia.Visuals/VisualTree/VisualExtensions.cs
@@ -377,7 +377,19 @@ namespace Avalonia.VisualTree
///
public static bool IsVisualAncestorOf(this IVisual visual, IVisual target)
{
- return target.GetVisualAncestors().Any(x => x == visual);
+ IVisual current = target?.VisualParent;
+
+ while (current != null)
+ {
+ if (current == visual)
+ {
+ return true;
+ }
+
+ current = current.VisualParent;
+ }
+
+ return false;
}
public static IEnumerable SortByZIndex(this IEnumerable elements)