avalonia-data-templates
Skill50 starsChanged 8 days agoMITView on GitHub
Use when defining DataTemplate, ItemTemplate, ContentTemplate, selecting templates by data type, using TreeDataTemplate for hierarchical data, implementing IDataTemplate selectors, or using FuncDataTemplate in Avalonia. Covers template scoping, app-level templates, and compiled bindings in templates (default in 12.0+, so `x:DataType` is required; `{ReflectionBinding}` as opt-out), including TableView/DataGrid column templates.
In this repository23 other files it gives its agents.
- avalonia-app-developmentSkill
- avalonia-controlsSkill
- avalonia-custom-controlsSkill
- avalonia-data-bindingSkill
- avalonia-deploymentSkill
- avalonia-eventsSkill
- avalonia-graphics-animationSkill
- avalonia-input-interactionSkill
- avalonia-interop-renderingSkill
- avalonia-layoutSkill
- avalonia-mobileSkill
- avalonia-mvvmSkill
- avalonia-performanceSkill
- avalonia-pro-maxSkill
- avalonia-property-systemSkill
- avalonia-servicesSkill
- avalonia-stylingSkill
- avalonia-testingSkill
- avalonia-threadingSkill
- avalonia-versionsSkill
- avalonia-wpf-migrationSkill
- avalonia-xamlSkill
- avaloniaSkill
The file
We split this skill into the rules it gives an agent
2 rules. About 0 are common, 1 is distinctive, and 1 is very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 170
Use bindings to the inherited DataContext, as in the binding example above.
- Distinctive: line 173
Check an A → B → A reuse sequence, including a transition from an active/error state to a plain item.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Avalonia Data Templates
Overview
DataTemplates define how a data object is rendered as UI. They apply to:
ItemsControland subclasses (ListBox,TreeView) viaItemTemplateTableViewColumn.CellTemplate(12.1+) and DataGrid template columns (see Column Templates below)ContentControland subclasses viaContentTemplate- Any
Contentproperty — Avalonia walks up the template tree to find a matchingDataTemplate
Template matching is by the data object's runtime type. First matching template in scope wins.
Inline DataTemplate
<ListBox ItemsSource="{Binding People}">
<ListBox.ItemTemplate>
<DataTemplate x:DataType="vm:PersonViewModel">
<StackPanel Orientation="Horizontal" Spacing="8">
<TextBlock Text="{Binding FirstName}"/>
<TextBlock Text="{Binding LastName}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>x:DataType enables compiled bindings inside the template.
12.0+: compiled bindings are the default (
AvaloniaUseCompiledBindingsByDefaultis on in new templates), so every{Binding}needs a knownx:DataTypeor the build fails (AVLN2000). For genuinely dynamic data use{ReflectionBinding Path}. 11.x: compiled bindings are opt-in (x:CompileBindings="True"/ the MSBuild property); without it{Binding}is reflection-based. Seeavalonia-versions.
ContentControl with DataTemplate
<ContentControl Content="{Binding CurrentPage}">
<ContentControl.ContentTemplate>
<DataTemplate x:DataType="vm:HomeViewModel">
<views:HomeView/>
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>For multi-page navigation, app-level templates (see below) are more practical than per-ContentControl templates.
App-Level Templates (Global Auto-Selection)
In App.axaml, templates registered here apply everywhere in the application. Avalonia automatically uses the matching template when a ViewModel type appears as Content or as an item.
<Application.DataTemplates>
<DataTemplate x:DataType="vm:HomeViewModel">
<views:HomeView/>
</DataTemplate>
<DataTemplate x:DataType="vm:SettingsViewModel">
<views:SettingsView/>
</DataTemplate>
<DataTemplate x:DataType="vm:PersonViewModel">
<StackPanel>
<TextBlock Text="{Binding FullName}" FontWeight="Bold"/>
<TextBlock Text="{Binding Email}"/>
</StackPanel>
</DataTemplate>
</Application.DataTemplates>With this setup, a ContentControl binding to a ViewModel just works:
<!-- Automatically renders the right view -->
<ContentControl Content="{Binding ActivePage}"/>Template Scope Resolution Order
Avalonia searches for a matching DataTemplate in this order:
ContentControl.ContentTemplateorItemsControl.ItemTemplate(explicit, highest priority)Control.DataTemplateson the control itselfDataTemplatesin parent controls, walking up the logical treeApplication.DataTemplates(global fallback)
First match wins. Order within a collection matters — put more specific types before base types.
DataTemplate Type Selector (IDataTemplate)
For conditional template selection based on runtime type or property values, implement IDataTemplate:
public class AnimalTemplateSelector : IDataTemplate
{
// Set these from XAML
public IDataTemplate? DogTemplate { get; set; }
public IDataTemplate? CatTemplate { get; set; }
public Control? Build(object? param) => param switch
{
Dog d => DogTemplate?.Build(d),
Cat c => CatTemplate?.Build(c),
_ => new TextBlock { Text = $"Unknown: {param}" }
};
public bool Match(object? data) => data is Dog or Cat;
}<Window.Resources>
<local:AnimalTemplateSelector x:Key="AnimalSelector">
<local:AnimalTemplateSelector.DogTemplate>
<DataTemplate x:DataType="vm:Dog">
<TextBlock Text="{Binding Breed}"/>
</DataTemplate>
</local:AnimalTemplateSelector.DogTemplate>
<local:AnimalTemplateSelector.CatTemplate>
<DataTemplate x:DataType="vm:Cat">
<TextBlock Text="{Binding Color}"/>
</DataTemplate>
</local:AnimalTemplateSelector.CatTemplate>
</local:AnimalTemplateSelector>
</Window.Resources>
<ItemsControl ItemsSource="{Binding Animals}"
ItemTemplate="{StaticResource AnimalSelector}"/>FuncDataTemplate (Code-Only)
Useful in code-behind, custom controls, or when XAML is impractical:
// Simple
var template = new FuncDataTemplate<PersonViewModel>((person, _) =>
new TextBlock { Text = person.FullName });
// With bindings
var template = new FuncDataTemplate<PersonViewModel>((person, scope) =>
{
var tb = new TextBlock();
// 12.0+: compiled binding from a lambda (no reflection)
tb.Bind(TextBlock.TextProperty,
CompiledBinding.Create((PersonViewModel p) => p.FullName));
// 11.x idiom / reflection fallback: new Binding(nameof(PersonViewModel.FullName))
// 12: new ReflectionBinding(nameof(PersonViewModel.FullName))
return tb;
});
myListBox.ItemTemplate = template;Recycling Without Stale State (11.3 / 12.1)
supportsRecycling: true is a correctness promise: FuncDataTemplate.Build(data, existing) returns the existing control without rerunning the factory. A one-time assignment such as Text = person.FullName or a handler capturing person can therefore keep the previous item's data.
- Use bindings to the inherited
DataContext, as in the binding example above. Avalonia's content presenter updates that context when it presents a different item; settingDataContext = personlocally on the returned view can pin the old item. - Rebind/reset any state that is not bound: custom classes, validation decorations, expanded details, and animation values. Use
DataContextChangedor a customIRecyclingDataTemplate.Build(data, existing)implementation for per-item work; factory initialization is not a reuse callback. - Unsubscribe from the previous item, cancel its asynchronous work, and reject late results before attaching the next item. Construction and visual-tree attachment alone do not identify every item change.
- Check an A → B → A reuse sequence, including a transition from an active/error state to a plain item. Keep recycling disabled when the view cannot safely switch items.
The recycling API requires existing to originate from the same template. See the 12.1.4 factory reuse implementation and content-presenter context update. A direct caller of Build(data, existing) must also update the data context; the factory does not do it.
Column Templates (TableView 12.1+, DataGrid)
<!-- TableView (12.1+) -->
<TableView ItemsSource="{Binding People}">
<TableView.Columns>
<TableViewColumn Header="Name" Binding="{Binding Name}"/>
<TableViewColumn Header="Status">
<TableViewColumn.CellTemplate>
<DataTemplate x:DataType="vm:PersonViewModel">
<TextBlock Text="{Binding Status}"/>
</DataTemplate>
</TableViewColumn.CellTemplate>
</TableViewColumn>
</TableView.Columns>
</TableView>CellTemplate takes priority over Binding. DataGrid (Avalonia.Controls.DataGrid, maintenance-only) uses DataGridTemplateColumn.CellTemplate the same way. Column Bindings are compiled in 12: the row type is inferred from ItemsSource when possible; otherwise set it explicitly with Binding="{Binding Name, DataType={x:Type vm:PersonViewModel}}" or fall back to {ReflectionBinding Name}. See avalonia-controls/data-display.
Reusable Templates as Resources
Define once, reference many times:
<Window.Resources>
<DataTemplate x:Key="PersonCard" x:DataType="vm:PersonViewModel">
<Border BorderThickness="1" CornerRadius="4" Padding="8">
<StackPanel>
<TextBlock Text="{Binding FullName}" FontWeight="Bold"/>
<TextBlock Text="{Binding Email}" Foreground="Gray"/>
</StackPanel>
</Border>
</DataTemplate>
</Window.Resources>
<!-- Use by key -->
<ListBox ItemsSource="{Binding People}"
ItemTemplate="{StaticResource PersonCard}"/>
<ContentControl Content="{Binding SelectedPerson}"
ContentTemplate="{StaticResource PersonCard}"/>TreeDataTemplate (Hierarchical / TreeView)
TreeDataTemplate extends DataTemplate with an ItemsSource binding for child nodes:
<TreeView ItemsSource="{Binding RootNodes}">
<TreeView.ItemTemplate>
<TreeDataTemplate x:DataType="vm:TreeNode"
ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal" Spacing="4">
<Image Source="{Binding Icon}" Width="16" Height="16"/>
<TextBlock Text="{Binding Name}"/>
</StackPanel>
</TreeDataTemplate>
</TreeView.ItemTemplate>
</TreeView>ViewModel:
public class TreeNode
{
public string Name { get; set; } = "";
public IImage? Icon { get; set; }
public ObservableCollection<TreeNode> Children { get; } = new();
}Mixed types in a tree (files and folders):
public class NodeTemplateSelector : IDataTemplate
{
public Control? Build(object? param) => param switch
{
FolderNode => /* build folder template */,
FileNode => /* build file template */,
_ => null
};
public bool Match(object? data) => data is FolderNode or FileNode;
}ItemsControl Panel Customization
Change the layout panel used to arrange items:
<!-- Wrap panel for wrapping items -->
<ItemsControl ItemsSource="{Binding Tags}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="vm:TagViewModel">
<Border Background="#E0E0E0" CornerRadius="12" Padding="8,4">
<TextBlock Text="{Binding Label}"/>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<!-- Uniform grid -->
<ItemsControl>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="3"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>Design-Time Data in Templates
<ListBox ItemsSource="{Binding People}">
<Design.DataContext>
<vm:MainViewModelDesign/>
</Design.DataContext>
<ListBox.ItemTemplate>
<DataTemplate x:DataType="vm:PersonViewModel">
<TextBlock Text="{Binding FullName}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>Common Mistakes
- Missing
x:DataTypeon DataTemplate — in 12.0+ (compiled bindings default) the build fails (AVLN2000) without a type context; in 11.x with compiled bindings enabled it fails the same way. Every DataTemplate using{Binding}needsx:DataType, or use{ReflectionBinding}. new Binding(...)in C# templates (12.0+) — the 11.x idiom is gone from the compiled-by-default model; useCompiledBinding.CreateorReflectionBinding.- Binding plugins like
{Binding ^Stream}(12.0+) — plugins were removed; seeavalonia-versionsfor replacements. - Wrong resource scope — a DataTemplate in
Window.Resourcesis not visible to templates inside a nestedUserControl. EachUserControlhas its own resource scope. Put shared templates inApplication.ResourcesorApplication.DataTemplates. - Using
DataTemplateinstead ofTreeDataTemplatefor tree data —DataTemplaterenders the node content but does not provide anItemsSourcebinding for children;TreeViewwill not recurse into children. - Matching on interface type — Avalonia's automatic type-based template selection matches on the concrete runtime type, not interfaces.
IDataTemplate.Match()must explicitly check for the concrete type, or register templates against the concrete type. - Multiple templates for same type in same scope — if two
DataTemplates inApplication.DataTemplateshavex:DataType="vm:PersonViewModel", the first one always wins. Remove duplicates or use keyed resources for multiple representations. - Circular template reference — a
DataTemplatethat creates aContentControlwhoseContentbinds back to the same ViewModel type causes infinite recursion. Use a keyed template or break the cycle with a wrapper ViewModel. - Setting both
ItemTemplateand using auto-selection — an explicitItemTemplatealways takes precedence overApplication.DataTemplates. RemoveItemTemplateto rely on global auto-selection.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

