diff --git a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
index 3e4204d79d..2f3f8c4daa 100644
--- a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
+++ b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
@@ -18,6 +18,7 @@
+
@@ -57,6 +58,7 @@
+
diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CompiledBindingPath.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CompiledBindingPath.cs
index 3324dd7319..aea83829ce 100644
--- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CompiledBindingPath.cs
+++ b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CompiledBindingPath.cs
@@ -37,6 +37,9 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
case ArrayElementPathElement arr:
node = new PropertyAccessorNode(CommonPropertyNames.IndexerName, enableValidation, new ArrayElementPlugin(arr.Indices, arr.ElementType));
break;
+ case VisualAncestorPathElement visualAncestor:
+ node = new FindVisualAncestorNode(visualAncestor.AncestorType, visualAncestor.Level);
+ break;
case AncestorPathElement ancestor:
node = new FindAncestorNode(ancestor.AncestorType, ancestor.Level);
break;
@@ -101,6 +104,11 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
_elements.Add(new AncestorPathElement(ancestorType, level));
return this;
}
+ public CompiledBindingPathBuilder VisualAncestor(Type ancestorType, int level)
+ {
+ _elements.Add(new VisualAncestorPathElement(ancestorType, level));
+ return this;
+ }
public CompiledBindingPathBuilder ElementName(INameScope nameScope, string name)
{
@@ -177,6 +185,18 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
public int Level { get; }
}
+ internal class VisualAncestorPathElement : ICompiledBindingPathElement, IControlSourceBindingPathElement
+ {
+ public VisualAncestorPathElement(Type ancestorType, int level)
+ {
+ AncestorType = ancestorType;
+ Level = level;
+ }
+
+ public Type AncestorType { get; }
+ public int Level { get; }
+ }
+
internal class ElementNameElement : ICompiledBindingPathElement, IControlSourceBindingPathElement
{
public ElementNameElement(INameScope nameScope, string name)
diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/FindVisualAncestorNode.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/FindVisualAncestorNode.cs
new file mode 100644
index 0000000000..5820f47fbb
--- /dev/null
+++ b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/FindVisualAncestorNode.cs
@@ -0,0 +1,52 @@
+using System;
+using Avalonia.Data.Core;
+using Avalonia.VisualTree;
+
+namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
+{
+ class FindVisualAncestorNode : ExpressionNode
+ {
+ private readonly int _level;
+ private readonly Type _ancestorType;
+ private IDisposable _subscription;
+
+ public FindVisualAncestorNode(Type ancestorType, int level)
+ {
+ _level = level;
+ _ancestorType = ancestorType;
+ }
+
+ public override string Description
+ {
+ get
+ {
+ if (_ancestorType == null)
+ {
+ return $"$visualparent[{_level}]";
+ }
+ else
+ {
+ return $"$visualparent[{_ancestorType.Name}, {_level}]";
+ }
+ }
+ }
+
+ protected override void StartListeningCore(WeakReference