Primary APIs:
Visual.VisualParentVisual.VisualChildrenVisual.AttachedToVisualTreeVisual.DetachedFromVisualTreeVisualExtensions.GetVisualAncestors(...)VisualExtensions.GetSelfAndVisualAncestors(...)VisualExtensions.FindAncestorOfType<T>(...)VisualExtensions.FindDescendantOfType<T>(...)VisualExtensions.GetVisualChildren(...)VisualExtensions.GetVisualDescendants(...)VisualExtensions.GetSelfAndVisualDescendants(...)VisualExtensions.GetVisualParent(...)VisualExtensions.GetVisualRoot(...)VisualExtensions.GetVisualAt(...)VisualExtensions.GetVisualsAt(...)VisualExtensions.GetTransformedBounds(...)VisualExtensions.IsAttachedToVisualTree(...)VisualExtensions.IsVisualAncestorOf(...)VisualTreeAttachmentEventArgsTopLevel.GetTopLevel(...)Reference source files:
src/Avalonia.Base/Visual.cssrc/Avalonia.Base/VisualTree/VisualExtensions.cssrc/Avalonia.Base/VisualTreeAttachmentEventArgs.cssrc/Avalonia.Controls/TopLevel.csThe visual tree is the rendered structure used for:
Key properties:
VisualParent: immediate rendered parent.VisualChildren: immediate rendered children.GetVisualRoot(): current IRenderRoot (often a TopLevel).Use the visual tree when reasoning about on-screen behavior.
using Avalonia.Controls;
using Avalonia.VisualTree;
var scroll = myControl.FindAncestorOfType<ScrollViewer>();
var button = myRoot.FindDescendantOfType<Button>();
using Avalonia.VisualTree;
foreach (var ancestor in myControl.GetSelfAndVisualAncestors())
{
Console.WriteLine(ancestor.GetType().Name);
}
using Avalonia.VisualTree;
foreach (var descendant in myRoot.GetVisualDescendants())
{
// inspection, diagnostics, selective filtering
}
using Avalonia.Controls;
TopLevel? host = TopLevel.GetTopLevel(myControl);
Point hit testing:
using Avalonia;
using Avalonia.VisualTree;
Point p = new Point(120, 40); // point in myRoot coordinate space
var first = myRoot.GetVisualAt(p);
var all = myRoot.GetVisualsAt(p, v => v.IsVisible);
Transformed bounds inspection:
using Avalonia.VisualTree;
var tb = myControl.GetTransformedBounds();
if (tb is { } bounds)
{
var clip = bounds.Clip;
var transform = bounds.Transform;
}
Use this when debugging clipping, transforms, or unexpected hit targets.
Attach/detach events are the correct points for visual-root-dependent work:
using Avalonia;
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
// e.Root, e.Parent available here
}
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnDetachedFromVisualTree(e);
// cleanup root-dependent subscriptions/resources
}
Guidance:
GetVisualRoot() is non-null before attachment.Quick visual tree dump helper:
using Avalonia.VisualTree;
static void DumpVisual(Visual visual, int depth = 0)
{
Console.WriteLine($"{new string(' ', depth * 2)}{visual.GetType().Name}");
foreach (var child in visual.GetVisualChildren())
{
DumpVisual(child, depth + 1);
}
}
Use in diagnostics, tests, and runtime asserts for template/overlay bugs.
includeSelf: true deliberately when searching from a known node.FindAncestorOfType returns null unexpectedly:
GetVisualRoot() is null: