Use HierarchicalTreeDataGridSource<TModel> when rows can contain children.
It provides expand/collapse behavior, hierarchical index mapping, and row expansion lifecycle events.
Use hierarchical source when:
Before, After, Inside)using System.Collections.ObjectModel;
using Avalonia.Controls;
using Avalonia.Controls.Models.TreeDataGrid;
public class Node
{
public string Name { get; set; } = string.Empty;
public ObservableCollection<Node> Children { get; } = new();
}
public class ExplorerViewModel
{
private readonly ObservableCollection<Node> _roots;
public ExplorerViewModel(ObservableCollection<Node> roots)
{
_roots = roots;
Source = new HierarchicalTreeDataGridSource<Node>(_roots)
{
Columns =
{
new HierarchicalExpanderColumn<Node>(
new TextColumn<Node, string>("Name", x => x.Name),
x => x.Children),
},
};
}
public HierarchicalTreeDataGridSource<Node> Source { get; }
}
A hierarchical source requires exactly one expander column (IExpanderColumn<TModel>).
Constraints enforced by source:
Programmatic expansion/collapse:
Source.Expand(new IndexPath(0));
Source.Collapse(new IndexPath(0, 2));
Source.ExpandAll();
Source.CollapseAll();
Predicate-based recursion:
Source.ExpandCollapseRecursive(model => ShouldExpand(model));
From a specific row:
Source.ExpandCollapseRecursive(row, model => model.Name.StartsWith("A"));
Useful hooks:
RowExpandingRowExpandedRowCollapsingRowCollapsedExample:
Source.RowExpanded += (_, e) =>
{
var row = e.Row;
// telemetry, lazy-load marker updates, etc.
};
if (Source.TryGetModelAt(new IndexPath(0, 1, 3), out var model))
{
// model resolved
}
Use this for commands that operate from selection/index paths.
using System.ComponentModel;
Source.SortBy(Source.Columns[0], ListSortDirection.Ascending);
Behavior:
Sorted event is raisedDragDropRows(...) supports:
BeforeAfterInsideConstraints:
IList<TModel>When dropping Inside, target row children collection is used.
HierarchicalExpanderColumn<TModel> can bind expanded state to model property by passing isExpandedSelector.
new HierarchicalExpanderColumn<FileNode>(
inner: new TextColumn<FileNode, string>("Name", x => x.Name),
childSelector: x => x.Children,
hasChildrenSelector: x => x.HasChildren,
isExpandedSelector: x => x.IsExpanded)
This pattern is ideal for file tree/explorer scenarios.
Feature behavior differs from expectations
Cause: one or more options in this scenario are configured differently (source type, column options, sort/selection/edit state).
Fix: compare your setup with the snippet in this article and verify runtime values on Source, Columns, and Selection.
Data changes are not visible in UI
Cause: model or collection notifications are missing, or a replaced collection/source is not re-bound.
Fix: ensure INotifyPropertyChanged/INotifyCollectionChanged flow is active and reassign Source after replacing underlying collections.