Learn how to effectively use data binding to create responsive and dynamic user interfaces in .NET MAUI applications.
.NET MAUI (Multi-platform App UI) provides a powerful data binding system that allows you to connect the UI elements of your application to your data models. This approach decouples the presentation layer from the business logic, leading to more maintainable, testable, and scalable code. Data binding simplifies common UI patterns such as:
At its heart, data binding involves two primary components:
INotifyPropertyChanged
.Label
, Entry
, Button
) and a specific property on that element (e.g., Text
, IsEnabled
, Command
) that you want to connect to the binding source.INotifyPropertyChanged
For data binding to work effectively and for the UI to update automatically when the underlying data changes, the binding source object must implement the INotifyPropertyChanged
interface. This interface defines a single event: PropertyChanged
. When a property's value changes, the object raises this event, notifying any listeners (including the data binding system) that the property has changed.
Here's a simple C# example:
public class MyDataModel : INotifyPropertyChanged
{
private string _message;
public string Message
{
get => _message;
set
{
if (_message != value)
{
_message = value;
OnPropertyChanged(nameof(Message));
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Data binding supports different modes that control the direction and behavior of data synchronization between the source and target.
Entry
or Slider
.The primary way to define data bindings in .NET MAUI is through XAML. You use the {Binding}
markup extension.
To bind the Text
property of a Label
to the Message
property of a MyDataModel
:
<!-- Assume MyDataModel is set as the BindingContext -->
<Label Text="{Binding Message}" />
For the binding to work, the UI element needs a BindingContext
. This is typically set to your ViewModel or data model instance.
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:YourApp.ViewModels"
x:Class="YourApp.Views.MyPage">
<ContentPage.BindingContext>
<local:MyViewModel />
</ContentPage.BindingContext>
<!-- ... rest of your page content ... -->
</ContentPage>
To allow user input in an Entry
to update a property in your ViewModel:
<Entry Text="{Binding UserName, Mode=TwoWay}" />
Note that the property in your ViewModel (UserName
in this case) must also raise PropertyChanged
when it changes.
Data binding is also crucial for displaying collections of data, such as lists or grids.
ItemsSource
BindingUse the ItemsSource
property of controls like ListView
, CollectionView
, or CarouselView
to bind to a collection.
<ListView ItemsSource="{Binding Items}" />
For the collection itself to notify the UI about additions or removals of items, it should implement INotifyCollectionChanged
or inherit from ObservableCollection<T>
.
ObservableCollection<T>
ObservableCollection<T>
is a built-in .NET collection that implements INotifyCollectionChanged
, making it ideal for binding to UI lists.
public class MyViewModel : INotifyPropertyChanged
{
private ObservableCollection<string> _items = new ObservableCollection<string>();
public ObservableCollection<string> Items
{
get => _items;
set
{
if (_items != value)
{
_items = value;
OnPropertyChanged(nameof(Items));
}
}
}
// ... INotifyPropertyChanged implementation ...
}
Commands provide a way to bind actions (like button clicks) to methods in your ViewModel, further enhancing the separation of concerns.
ICommand
InterfaceImplement the ICommand
interface in your ViewModel. A common implementation pattern is to use RelayCommand
or DelegateCommand
(often found in MVVM helper libraries) or to implement it manually.
public class MyViewModel : INotifyPropertyChanged
{
// ... other properties ...
public ICommand SaveCommand { get; }
public MyViewModel()
{
SaveCommand = new Command(ExecuteSave, CanExecuteSave);
}
private void ExecuteSave()
{
// Logic to save data
System.Diagnostics.Debug.WriteLine("Data Saved!");
}
private bool CanExecuteSave()
{
// Logic to determine if the command can be executed
return true; // Or based on some condition
}
// ... INotifyPropertyChanged implementation ...
}
<Button Text="Save" Command="{Binding SaveCommand}" />
The Command
property of controls like Button
, MenuItem
, etc., can be bound to an ICommand
instance.
You can navigate through object graphs using the Path
property in your binding.
<!-- Assuming a ViewModel with a property 'User' which has a 'Name' property -->
<Label Text="{Binding User.Name}" />
StringFormat
Format the bound string value using StringFormat
.
<Label Text="{Binding Price, StringFormat='{0:C2}'}" />
<Label Text="{Binding DateTime, StringFormat='{0:yyyy-MM-dd HH:mm}'}" />
When you need to transform data before displaying it or to convert it for binding, use IValueConverter
.
Example: A converter to turn a boolean into a visible/hidden state.
public class BoolToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is bool boolValue)
{
return boolValue ? Visibility.Visible : Visibility.Collapsed;
}
return Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is Visibility visibilityValue)
{
return visibilityValue == Visibility.Visible;
}
return false;
}
}
Register the converter in your XAML and use it:
<ContentPage.Resources>
<local:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
</ContentPage.Resources>
<!-- ... -->
<StackLayout IsVisible="{Binding IsLoading, Converter={StaticResource BoolToVisibilityConverter}}" />
INotifyPropertyChanged
correctly: Ensure your properties raise the PropertyChanged
event for automatic UI updates.ObservableCollection<T>
for collections: This makes list updates seamless.