Skip to main content
Avalonia’s data binding system connects UI properties to data sources, enabling reactive, declarative applications with automatic synchronization between views and view models.

Binding Fundamentals

Data binding creates a connection between a target property (usually a UI control) and a source value (typically from a ViewModel).

Basic Binding Syntax

<TextBlock Text="{Binding UserName}" />

<!-- With explicit properties -->
<TextBlock Text="{Binding Path=UserName, Mode=OneWay}" />

<!-- Binding to nested properties -->
<TextBlock Text="{Binding User.Profile.DisplayName}" />

Binding Components

A binding consists of several key components:
ReflectionBinding Properties (from source)
public class ReflectionBinding : BindingBase
{
    // The property path to bind to
    public string Path { get; set; }
    
    // Direction of data flow
    public BindingMode Mode { get; set; }
    
    // Binding source
    public object? Source { get; set; }
    
    // Value converter
    public IValueConverter? Converter { get; set; }
    
    // Converter parameter
    public object? ConverterParameter { get; set; }
    
    // Fallback value when binding fails
    public object? FallbackValue { get; set; }
    
    // Value to use when source is null
    public object? TargetNullValue { get; set; }
    
    // String format for display
    public string? StringFormat { get; set; }
    
    // When to update the source
    public UpdateSourceTrigger UpdateSourceTrigger { get; set; }
    
    // Delay before updating source (milliseconds)
    public int Delay { get; set; }
}

Binding Modes

Avalonia supports multiple binding modes that control data flow:

OneWay

Data flows from source to target only. Target updates when source changes.

TwoWay

Data flows both directions. Changes in either source or target update the other.

OneTime

Data flows once when binding is created, then never updates.

OneWayToSource

Data flows from target to source only. Source updates when target changes.

Mode Examples

Binding Modes
<!-- OneWay: Display data (default for most properties) -->
<TextBlock Text="{Binding Status}" />

<!-- TwoWay: Input controls (default for TextBox.Text, etc.) -->
<TextBox Text="{Binding UserName, Mode=TwoWay}" />

<!-- OneTime: Static data -->
<TextBlock Text="{Binding AppVersion, Mode=OneTime}" />

<!-- OneWayToSource: Write-only binding -->
<Slider Value="{Binding Volume, Mode=OneWayToSource}" />
Each property has a default binding mode. TextBox.Text defaults to TwoWay, while TextBlock.Text defaults to OneWay.

Binding Sources

DataContext

The default binding source is the DataContext:
<Window xmlns="https://github.com/avaloniaui"
        x:Class="MyApp.MainWindow">
    <!-- Bindings resolve against DataContext -->
    <StackPanel>
        <TextBlock Text="{Binding Title}" />
        <TextBlock Text="{Binding Description}" />
    </StackPanel>
</Window>

DataContext Inheritance

DataContext flows down the visual tree:
DataContext Inheritance
<Window DataContext="{Binding WindowViewModel}">
    <!-- Inherits Window's DataContext -->
    <StackPanel>
        <!-- Can override DataContext -->
        <UserControl DataContext="{Binding ChildViewModel}">
            <!-- Binds to ChildViewModel properties -->
            <TextBlock Text="{Binding ChildProperty}" />
        </UserControl>
        
        <!-- Still uses Window's DataContext -->
        <TextBlock Text="{Binding WindowProperty}" />
    </StackPanel>
</Window>

Explicit Source

<StackPanel>
    <Slider x:Name="VolumeSlider" Minimum="0" Maximum="100" />
    
    <!-- Bind to named element -->
    <TextBlock Text="{Binding #VolumeSlider.Value}" />
    <!-- or -->
    <TextBlock Text="{Binding Value, ElementName=VolumeSlider}" />
</StackPanel>

Value Conversion

Converters transform values between source and target:

Built-in Converters

Avalonia provides several built-in converters:
Using Built-in Converters
<!-- Boolean negation -->
<Button IsEnabled="{Binding !IsProcessing}" />

<!-- Null check -->
<Border IsVisible="{Binding User, Converter={x:Static ObjectConverters.IsNotNull}}" />

<!-- String format -->
<TextBlock Text="{Binding Price, StringFormat='${0:F2}'}" />
<TextBlock Text="{Binding Date, StringFormat='Born on {0:yyyy-MM-dd}'}" />

Custom Converters

using Avalonia.Data.Converters;
using Avalonia.Media;
using System;
using System.Globalization;

public class BoolToColorConverter : IValueConverter
{
    public object? Convert(object? value, Type targetType, 
        object? parameter, CultureInfo culture)
    {
        if (value is bool boolValue)
        {
            return boolValue 
                ? Brushes.Green 
                : Brushes.Red;
        }
        return Brushes.Gray;
    }

    public object? ConvertBack(object? value, Type targetType, 
        object? parameter, CultureInfo culture)
    {
        // Not implemented for one-way conversion
        throw new NotImplementedException();
    }
}

Converter Parameters

Parameterized Converter
<TextBlock Foreground="{Binding Status, 
    Converter={StaticResource StatusToColorConverter},
    ConverterParameter='Inverted'}" />
Using Parameter
public object? Convert(object? value, Type targetType, 
    object? parameter, CultureInfo culture)
{
    bool invert = parameter?.ToString() == "Inverted";
    // Use parameter in conversion logic
}

Update Source Trigger

Controls when the binding updates the source:
UpdateSourceTrigger
<!-- Update on every keystroke (default) -->
<TextBox Text="{Binding SearchQuery, UpdateSourceTrigger=PropertyChanged}" />

<!-- Update when control loses focus -->
<TextBox Text="{Binding UserName, UpdateSourceTrigger=LostFocus}" />

<!-- Update explicitly via code -->
<TextBox Text="{Binding Value, UpdateSourceTrigger=Explicit}" />
UpdateSourceTrigger Values
public enum UpdateSourceTrigger
{
    Default,         // Use property default
    PropertyChanged, // Update on every change
    LostFocus,      // Update when focus lost
    Explicit        // Update via UpdateSource() call
}

Binding Delays

Debounce rapid updates:
Delayed Binding
<!-- Wait 500ms after typing stops -->
<TextBox Text="{Binding SearchQuery, Delay=500}" />
Use Delay for search boxes or expensive operations to avoid updating on every keystroke.

Compiled Bindings

Compiled bindings provide better performance and compile-time checking:
Compiled Bindings
<Window xmlns="https://github.com/avaloniaui"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:vm="using:MyApp.ViewModels"
        x:Class="MyApp.MainWindow"
        x:DataType="vm:MainWindowViewModel"
        x:CompileBindings="True">
    
    <!-- Type-safe bindings with IntelliSense -->
    <StackPanel>
        <TextBlock Text="{Binding UserName}" />
        <TextBlock Text="{Binding Email}" />
        <!-- Compile error if property doesn't exist! -->
    </StackPanel>
</Window>
With compiled bindings enabled, typos in property names become compile errors instead of runtime failures.

Per-Binding DataType

Inline DataType
<ItemsControl ItemsSource="{Binding Users}">
    <ItemsControl.ItemTemplate>
        <DataTemplate DataType="vm:UserViewModel">
            <!-- Compiled bindings within template -->
            <TextBlock Text="{Binding UserName}" />
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

Fallback Values

FallbackValue

Used when binding fails:
FallbackValue
<TextBlock Text="{Binding UserName, FallbackValue='Guest'}" />

<!-- Useful for design-time -->
<Image Source="{Binding ProfileImage, FallbackValue='/Assets/default-avatar.png'}" />

TargetNullValue

Used when source value is null:
TargetNullValue
<TextBlock Text="{Binding Description, TargetNullValue='No description'}" />

Multi-Binding

Combine multiple bindings:
MultiBinding
<TextBlock>
    <TextBlock.Text>
        <MultiBinding Converter="{StaticResource FullNameConverter}">
            <Binding Path="FirstName" />
            <Binding Path="LastName" />
        </MultiBinding>
    </TextBlock.Text>
</TextBlock>
MultiValue Converter
using Avalonia.Data.Converters;

public class FullNameConverter : IMultiValueConverter
{
    public object? Convert(IList<object?> values, Type targetType, 
        object? parameter, CultureInfo culture)
    {
        if (values.Count >= 2 && 
            values[0] is string first && 
            values[1] is string last)
        {
            return $"{first} {last}";
        }
        return string.Empty;
    }
}

Observable Collections

Use ObservableCollection<T> for collection bindings:
ViewModel with Observable Collection
using System.Collections.ObjectModel;
using ReactiveUI;

public class MainWindowViewModel : ViewModelBase
{
    public ObservableCollection<TodoItem> TodoItems { get; }
    
    public MainWindowViewModel()
    {
        TodoItems = new ObservableCollection<TodoItem>();
    }
    
    public void AddItem(string title)
    {
        // UI automatically updates
        TodoItems.Add(new TodoItem { Title = title });
    }
}
Binding to Collection
<ListBox ItemsSource="{Binding TodoItems}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding Title}" />
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

Property Change Notification

INotifyPropertyChanged

using System.ComponentModel;
using System.Runtime.CompilerServices;

public class ViewModel : INotifyPropertyChanged
{
    private string _userName = string.Empty;
    
    public string UserName
    {
        get => _userName;
        set
        {
            if (_userName != value)
            {
                _userName = value;
                OnPropertyChanged();
            }
        }
    }
    
    public event PropertyChangedEventHandler? PropertyChanged;
    
    protected virtual void OnPropertyChanged(
        [CallerMemberName] string? propertyName = null)
    {
        PropertyChanged?.Invoke(this, 
            new PropertyChangedEventArgs(propertyName));
    }
}

Binding Diagnostics

Enable binding diagnostics for troubleshooting:
Enable Diagnostics
using Avalonia.Logging;

// In Program.cs
public static AppBuilder BuildAvaloniaApp()
    => AppBuilder.Configure<App>()
        .UsePlatformDetect()
        .LogToTrace(LogEventLevel.Debug) // Enable debug logging
        .WithInterFont();
Binding errors appear in debug output:
[Binding] Error in binding to 'TextBlock.Text': Could not find property 'NonExistentProperty' on 'MainWindowViewModel'

Best Practices

1

Use compiled bindings

Enable x:CompileBindings="True" and set x:DataType for type safety and performance.
2

Implement INotifyPropertyChanged

Use ReactiveUI’s ViewModelBase or implement INotifyPropertyChanged for all bindable properties.
3

Choose appropriate binding modes

Use OneWay for read-only data, TwoWay for editable properties.
4

Use ObservableCollection for lists

Always use ObservableCollection<T> instead of List<T> for collection bindings.
5

Provide fallback values

Set FallbackValue and TargetNullValue for robust UI.

Build docs developers (and LLMs) love