Skip to main content

How to bind to a collection

When your application needs to display a dynamic list of items, you bind a collection property to a list control such as ListBox, ItemsControl, or ComboBox. By using ObservableCollection<T>, the UI stays in sync whenever you add, remove, or reorder items. This guide walks you through the common scenarios for collection binding in Avalonia.

Why use ObservableCollection<T>

A standard List<T> does not notify the UI when its contents change. If you add an item to a List<T> at runtime, the control will not update. ObservableCollection<T> implements INotifyCollectionChanged, which raises events that Avalonia listens for so the bound control can refresh automatically.

Use ObservableCollection<T> when:

  • Items are added or removed after the initial load.
  • You need the UI to reflect changes without manually rebinding.

If your collection is static (loaded once and never modified), a simple List<T> or array works fine.

Bind to a simple ObservableCollection

Start with an ObservableCollection<string> bound to a ListBox.

Define the collection in your view model:

public class MainViewModel : ObservableObject
{
private ObservableCollection<string> _items;

public ObservableCollection<string> Items
{
get => _items;
set => SetProperty(ref _items, value);
}

public MainViewModel()
{
Items = new ObservableCollection<string> { "Item 1", "Item 2", "Item 3" };
}
}

Bind the collection to a ListBox in your AXAML:

<ListBox ItemsSource="{Binding Items}" />

When you call Items.Add("Item 4") in your view model, the ListBox displays the new entry immediately.

Bind to a collection of complex objects

When your collection contains objects with multiple properties, use a DataTemplate to control how each item appears. For property changes on individual items to propagate to the UI, each item class must also implement change notification.

Define a Person class that extends ObservableObject:

public class Person : ObservableObject
{
private string _name;
private int _age;

public string Name
{
get => _name;
set => SetProperty(ref _name, value);
}

public int Age
{
get => _age;
set => SetProperty(ref _age, value);
}
}

Expose an ObservableCollection<Person> from your view model:

public class MainViewModel : ObservableObject
{
private ObservableCollection<Person> _people;

public ObservableCollection<Person> People
{
get => _people;
set => SetProperty(ref _people, value);
}

public MainViewModel()
{
People = new ObservableCollection<Person>
{
new Person { Name = "John Doe", Age = 30 },
new Person { Name = "Jane Doe", Age = 28 }
};
}
}

Bind the collection to a ListBox with a DataTemplate:

<ListBox ItemsSource="{Binding People}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" Margin="0,0,10,0" />
<TextBlock Text="{Binding Age}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

Each Person appears with their Name and Age side by side. Because Person extends ObservableObject, editing a person's Name or Age in code updates the corresponding ListBox row without any extra work.

Add and remove items at runtime

A common pattern is to pair collection binding with commands that let the user add or remove entries:

public class MainViewModel : ObservableObject
{
public ObservableCollection<string> Items { get; } = new();

public ICommand AddItemCommand { get; }
public ICommand RemoveItemCommand { get; }

public MainViewModel()
{
Items.Add("First item");

AddItemCommand = new RelayCommand(() =>
{
Items.Add($"Item {Items.Count + 1}");
});

RemoveItemCommand = new RelayCommand(() =>
{
if (Items.Count > 0)
Items.RemoveAt(Items.Count - 1);
},
() => Items.Count > 0);
}
}
<DockPanel>
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal" Spacing="8" Margin="0,0,0,8">
<Button Content="Add" Command="{Binding AddItemCommand}" />
<Button Content="Remove" Command="{Binding RemoveItemCommand}" />
</StackPanel>
<ListBox ItemsSource="{Binding Items}" />
</DockPanel>

Use ItemsControl for non-selectable lists

If you do not need selection behavior, use ItemsControl instead of ListBox. It renders each item without selection highlighting or keyboard navigation:

<ItemsControl ItemsSource="{Binding People}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Padding="8" Margin="0,0,0,4" Background="#f0f0f0" CornerRadius="4">
<TextBlock Text="{Binding Name}" />
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

Common pitfalls

ProblemCauseSolution
UI does not update when items are addedUsing List<T> instead of ObservableCollection<T>Switch to ObservableCollection<T>
UI does not update when a property on an item changesItem class does not implement INotifyPropertyChangedHave your item class extend ObservableObject or implement INotifyPropertyChanged
Replacing the entire collection does not update the UIThe collection property lacks change notificationUse SetProperty (or the [ObservableProperty] attribute) for the property that holds the collection

See also