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..bd05796578 100644
--- a/src/Avalonia.Styling/LogicalTree/LogicalExtensions.cs
+++ b/src/Avalonia.Styling/LogicalTree/LogicalExtensions.cs
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
-using System.Linq;
namespace Avalonia.LogicalTree
{
@@ -29,6 +28,35 @@ 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;
+ }
+
public static IEnumerable GetLogicalChildren(this ILogical logical)
{
return logical.LogicalChildren;
@@ -57,6 +85,28 @@ 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);
+ }
+
public static ILogical GetLogicalParent(this ILogical logical)
{
return logical.LogicalParent;
@@ -80,9 +130,46 @@ namespace Avalonia.LogicalTree
}
}
- public static bool IsLogicalParentOf(this ILogical logical, ILogical target)
+ 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..636aa6836e 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;
+
+ while (current != null)
+ {
+ if (current == visual)
+ {
+ return true;
+ }
+
+ current = current.VisualParent;
+ }
+
+ return false;
}
public static IEnumerable SortByZIndex(this IEnumerable elements)