avalonia-data-binding
Skill50 starsChanged 7 days agoMITView on GitHub
Use when writing data bindings, compiled bindings, converters, MultiBinding, collection views, validation, or binding to commands in Avalonia. Covers binding modes, path syntax, DataContext, built-in and custom converters, FallbackValue, DataValidationException, and debugging techniques.
In this repository23 other files it gives its agents.
- avalonia-app-developmentSkill
- avalonia-controlsSkill
- avalonia-custom-controlsSkill
- avalonia-data-templatesSkill
- 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
1 rule. About 0 are common, 0 are distinctive, and 1 is very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈0
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 316
Check TraceLevel on bindings: {Binding Name, TraceLevel=Verbose}.
≈ “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 Binding
Overview
(12.0+) compiled bindings are on by default (AvaloniaUseCompiledBindingsByDefault defaults true). {Binding} without x:DataType fails to compile. Compiled bindings are verified at build time and generate no reflection. Opt out per scope with x:CompileBindings="False", per binding with {ReflectionBinding}, or globally with <AvaloniaUseCompiledBindingsByDefault>false</AvaloniaUseCompiledBindingsByDefault>.
(11.x) compiled bindings are opt-in: set <AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault> or x:CompileBindings="True"; otherwise {Binding} is reflection-based and {CompiledBinding} must be explicit.
Binding API in C# code
| 11.x | 12.0+ |
IBinding | BindingBase (ReflectionBinding, CompiledBinding, TemplateBinding, IndexerBinding derive from it) |
InstancedBinding | BindingExpressionBase |
new Binding("Path") | new ReflectionBinding("Path") (Binding kept for compatibility = ReflectionBinding) or CompiledBinding |
// (12.0+) typed compiled binding from a lambda
var b = CompiledBinding.Create((Item item) => item.Value);Binding plugins (BindingPlugins) are removed in 12; data-annotations validation is off by default (11.x: on).
Binding Modes
| Mode | Direction | Updates |
OneWay | Source → Target | On source change |
TwoWay | Source ↔ Target | On either change |
OneTime | Source → Target | Once at bind time |
OneWayToSource | Target → Source | On target change |
Default | Per-property default | Typically TwoWay for input controls, OneWay for display |
Basic Binding Syntax
<!-- Compiled binding — requires x:DataType on scope root -->
<Window xmlns:vm="using:MyApp.ViewModels"
x:DataType="vm:MainViewModel">
<TextBox Text="{Binding Name}"/>
<TextBlock Text="{Binding Name, Mode=OneWay}"/>
<TextBlock Text="{Binding Name, StringFormat='Hello, {0}!'}"/>
</Window>
<!-- Opt out per-binding (reflection binding) -->
<TextBox Text="{Binding Name, x:CompileBindings=False}"/>
<!-- Explicit compiled binding syntax (default in 12.0+; required in 11.x unless compiled-by-default is set) -->
<TextBox Text="{CompiledBinding Name}"/>Binding Path Syntax
| Syntax | Meaning |
{Binding Name} | Property on DataContext |
{Binding Address.Street} | Nested property path |
{Binding [0]} | Indexer |
{Binding [key]} | Dictionary indexer |
{Binding .} | DataContext itself |
{Binding #controlName.Property} | Named control in same scope |
{Binding $parent.Property} | Nearest parent control |
{Binding $parent[Window].Property} | Nearest parent of type Window |
{Binding $parent[1].Property} | Parent N levels up |
{Binding $self.Property} | The control itself |
{Binding $root.Property} | Root of logical tree |
DataContext
Inherits down the logical tree. Child controls automatically receive the parent DataContext unless overridden.
<!-- Set in XAML -->
<Window DataContext="{x:Static vm:DesignData.Main}"
x:DataType="vm:MainViewModel">
<!-- Design-time only data context -->
<Design.DataContext>
<vm:MainViewModelDesign/>
</Design.DataContext>// Set in code-behind
public MainWindow()
{
InitializeComponent();
DataContext = new MainViewModel();
}x:DataType informs the compiler what type the DataContext is. Required for every scope (Window, UserControl, DataTemplate) that uses compiled bindings.
Converters
Built-in (namespace Avalonia.Data.Converters)
| Converter | Usage |
BooleanToVisibilityConverter | bool → IsVisible (rarely needed; bind IsVisible to bool directly) |
StringConverters.IsNullOrEmpty | string → bool |
StringConverters.IsNotNullOrEmpty | string → bool |
ObjectConverters.IsNull | object → bool |
ObjectConverters.IsNotNull | object → bool |
FuncValueConverter<TIn,TOut> | Inline lambda converter |
<TextBlock IsVisible="{Binding Name, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>FuncValueConverter (inline, no class needed)
// In ViewModel or resource file
public static readonly FuncValueConverter<int, string> CountToLabel =
new(count => count == 1 ? "item" : "items");<TextBlock Text="{Binding Count, Converter={x:Static vm:Converters.CountToLabel}}"/>Custom IValueConverter
public class InverseBoolConverter : IValueConverter
{
public static readonly InverseBoolConverter Instance = new();
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
=> value is bool b ? !b : BindingOperations.DoNothing;
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
=> Convert(value, targetType, parameter, culture);
}<!-- Register as resource -->
<Window.Resources>
<converters:InverseBoolConverter x:Key="InverseBool"/>
</Window.Resources>
<Button IsEnabled="{Binding IsBusy, Converter={StaticResource InverseBool}}"/>Converter with Parameter
<TextBlock Text="{Binding Price, Converter={StaticResource CurrencyConverter}, ConverterParameter=USD}"/>MultiBinding
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="{}{0} {1}">
<Binding Path="FirstName"/>
<Binding Path="LastName"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>FuncMultiValueConverter<TIn,TOut> lambda parameter is IReadOnlyList<TIn> in 12.0+ (IEnumerable<TIn> in 11.x) — indexable (values[0]); update lambdas that declare IEnumerable<TIn>.
With a custom IMultiValueConverter:
public class FullNameConverter : IMultiValueConverter
{
public object? Convert(IList<object?> values, Type targetType, object? parameter, CultureInfo culture)
=> values is [string first, string last] ? $"{first} {last}" : null;
}FallbackValue and TargetNullValue
<!-- TargetNullValue: shown when binding value is null -->
<TextBlock Text="{Binding Description, TargetNullValue='No description'}"/>
<!-- FallbackValue: shown when binding fails entirely (path missing, exception) -->
<Image Source="{Binding AvatarUrl, FallbackValue={StaticResource DefaultAvatar}}"/>Binding to Collections
<ListBox ItemsSource="{Binding Items}"/>
<DataGrid ItemsSource="{Binding Items}"/>For sorting and filtering, use DataGridCollectionView:
var view = new DataGridCollectionView(myObservableList);
view.SortDescriptions.Add(new SortDescription("Name", ListSortDirection.Ascending));
view.Filter = item => ((MyItem)item).IsActive;
DataContext = new { Items = view };For reactive filtering without DataGridCollectionView, use a computed IEnumerable property that re-exposes a filtered collection and raise PropertyChanged when the filter changes.
Binding Validation
Throw DataValidationException from the property setter:
private string _name = "";
public string Name
{
get => _name;
set
{
if (string.IsNullOrWhiteSpace(value))
throw new DataValidationException("Name cannot be empty.");
SetAndRaise(NameProperty, ref _name, value); // AvaloniaObject
// or for INPC:
_name = value;
OnPropertyChanged();
}
}Data-annotations validation is off by default in 12.0+ (on in 11.x). Exceptions thrown in setters (DataValidationException) and INotifyDataErrorInfo still surface. For AvaloniaObject DirectProperty, enableDataValidation: true makes the property report errors automatically (12.0+):
public static readonly DirectProperty<MyControl, string> NameProperty =
AvaloniaProperty.RegisterDirect<MyControl, string>(
nameof(Name), o => o.Name, (o, v) => o.Name = v,
enableDataValidation: true);Display validation errors with the built-in control:
<DataValidationErrors/>Or access errors via the attached property DataValidationErrors.GetErrors(control).
Binding to Commands
<Button Command="{Binding SaveCommand}"/>
<Button Command="{Binding DeleteCommand}" CommandParameter="{Binding SelectedItem}"/>
<!-- Binding to a method directly (ReactiveUI / CommunityToolkit) -->
<Button Command="{Binding DoSomething}"/>With IRelayCommand (CommunityToolkit.Mvvm):
[RelayCommand]
private async Task SaveAsync()
{
// ...
}
// Generates SaveCommand property automaticallyWith ReactiveCommand (ReactiveUI):
public ICommand SaveCommand { get; }
SaveCommand = ReactiveCommand.CreateFromTask(SaveAsync, canSave);Binding in ItemTemplate (DataTemplate scope)
Each item in a collection gets its own DataContext. Declare x:DataType on the DataTemplate:
<ListBox ItemsSource="{Binding People}">
<ListBox.ItemTemplate>
<DataTemplate x:DataType="vm:PersonViewModel">
<TextBlock Text="{Binding FullName}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>Binding Debugging
<!-- Enable trace output for a specific binding -->
<TextBlock Text="{Binding Name, diag:Binding.DebugMode=True}"
xmlns:diag="clr-namespace:Avalonia.Diagnostics;assembly=Avalonia"/>- DevTools (F12 in Debug builds) — (12.0+)
AvaloniaUI.DiagnosticsSupportpackage +this.AttachDeveloperTools()inApp; (11.x)Avalonia.Diagnosticspackage +this.AttachDevTools()(package does not exist for 12). Data Context panel shows current DataContext hierarchy and property values. - Output window: Binding errors appear as warnings, e.g.
[Binding] Could not find a matching property accessor for 'Name'. - Check
TraceLevelon bindings:{Binding Name, TraceLevel=Verbose}.
Common Mistakes
- Missing
x:DataType— compiled bindings fail at build time with a type-resolution error. Every scope (Window, UserControl, DataTemplate) that uses compiled bindings needsx:DataType. - Binding to non-public members — compiled bindings require
publicproperties. Internal or private members cause build errors. - Not implementing
INotifyPropertyChanged— binding updates are silently dropped. UseObservableObject(CommunityToolkit),ReactiveObject(ReactiveUI), orAvaloniaObjectwithSetAndRaise. - Using
OneTimeexpecting updates —OneTimecaptures the value once at binding activation. UseOneWayfor live updates. - Writing
new Binding(...)/IBindingin 12 code — useReflectionBinding/CompiledBindingandBindingBase. - Relying on
BindingPluginsor implicit annotation validation in 12 — removed / off by default. - Mixing
{Binding}and{CompiledBinding}carelessly — in Avalonia 12,{Binding}is compiled by default (in 11.x it is reflection unless configured). Opt out explicitly withx:CompileBindings=Falseon the element, not by switching to{CompiledBinding}(they are the same thing). - Reflection binding path with compiled binding scope — special paths like
$parent,$self,#namework only in compiled bindings and require the target type to be resolvable. - Binding
Commandto a method name, not a property —Commandmust bind to anICommandproperty. Methods need to be wrapped in a command object.
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.

