Skip to main content

Control trees

Avalonia creates control trees from the XAML files in an application so that it can render the UI presentation and manage the application functionality.

Logical tree

The logical control tree represents the application controls (including the main window) in the hierarchy in which they are defined in the XAML. For example, a control (button) inside another control (stack panel) in a window will have the 3-layer logical tree shown here:

Logical tree structure of an Avalonia window

While your application is running, you can show the Avalonia Dev Tools window (press F12). This displays the logical tree on its Logical Tree tab.

Visual tree

The visual control tree contains everything that is actually being run by Avalonia. It shows all the properties set on the controls, and all the additional parts that have been added by Avalonia to present the UI and manage the application functionality.

Visual tree structure showing template-expanded controls

You can see the visual control tree on the Visual Tree tab of the Avalonia Dev Tools window.

Events

An essential part of application functionality management performed by Avalonia, is the generation and propagation of events. The Events tab logs the source and propagation of events as you move around, and otherwise interact with the running application.

Event routing through the control tree

Logical tree vs visual tree

The two trees serve different purposes and contain different levels of detail.

AspectLogical TreeVisual Tree
ContainsControls as declared in XAMLAll rendered elements including template parts
Template expansionNo (templates are single nodes)Yes (templates are expanded into parts)
Used forData context inheritance, resource lookupRendering, hit testing, layout
Navigation APILogicalExtensions.GetLogicalParent()VisualExtensions.GetVisualParent()

Avalonia provides extension methods for walking both trees programmatically.

// Walk up the logical tree
var parent = myControl.GetLogicalParent();

// Walk up the visual tree
var visualParent = myControl.GetVisualParent();

// Find a specific ancestor in the logical tree
var window = myControl.FindLogicalAncestorOfType<Window>();

// Find a specific ancestor in the visual tree
var border = myControl.FindAncestorOfType<Border>();

// Find an ancestor or descendant matching a predicate
var enabledPanel = myControl.FindAncestorOfType<StackPanel>(
predicate: p => p.IsEnabled);
var visibleTextBox = myControl.FindDescendantOfType<TextBox>(
predicate: tb => tb.IsVisible);

// Get all logical children
var children = myControl.GetLogicalChildren();

Tree traversal in custom controls

When building custom controls, you often need to respond to a control being added to or removed from a tree. Override these methods to hook into tree lifecycle events:

  • OnAttachedToLogicalTree / OnDetachedFromLogicalTree for setup and cleanup of data bindings, subscriptions, or inherited properties.
  • OnAttachedToVisualTree / OnDetachedFromVisualTree for rendering-related setup such as acquiring platform resources.
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
// Control is now part of the visual tree and can be rendered
}

protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnDetachedFromVisualTree(e);
// Clean up rendering resources
}

See also