Data Binding in .NET MAUI MVVM
Data binding is a core concept in the Model-View-ViewModel (MVVM) architectural pattern. It establishes a connection between properties of your ViewModels and the UI elements in your Views, allowing for automatic synchronization of data. This tutorial will guide you through the fundamentals of data binding in .NET MAUI.
What is Data Binding?
Data binding is a mechanism that connects two objects, such as a ViewModel property and a UI element property, so that they can communicate. When one property changes, the other is automatically updated. This significantly reduces boilerplate code for updating the UI manually.
In .NET MAUI, data binding is declarative and is typically defined in XAML.
Binding Modes
Data binding supports various modes to control the direction and timing of data synchronization:
- OneWay: Changes in the source property update the target property. This is the default mode.
- TwoWay: Changes in the source property update the target property, and changes in the target property update the source property.
- OneTime: The target property is initialized with the source property's value once. No further updates occur.
- OneWayToSource: Changes in the target property update the source property.
Basic Data Binding
The most common scenario is binding a UI element's property to a property in your ViewModel. Let's say you have a ViewModel with a Message property and you want to display it in a Label.
ViewModel Code (Example)
public class MainViewModel : 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));
}
public MainViewModel()
{
Message = "Hello from MVVM!";
}
}
XAML View Code (Example)
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:viewmodels="clr-namespace:YourApp.ViewModels"
x:Class="YourApp.Views.MainPage">
<ContentPage.BindingContext>
<viewmodels:MainViewModel />
</ContentPage.BindingContext>
<StackLayout VerticalOptions="Center" HorizontalOptions="Center">
<Label Text="{Binding Message}" />
</StackLayout>
</ContentPage>
In this example:
- We set the
BindingContextof theContentPageto an instance ofMainViewModel. - The
Label'sTextproperty is bound to theMessageproperty of theBindingContextusing{Binding Message}. - The ViewModel implements
INotifyPropertyChanged, which is crucial for the UI to be notified of property changes.
Two-Way Binding
Two-way binding is useful when you want user input in a UI element (like an Entry) to update a ViewModel property, and vice-versa.
XAML View Code (Example)
<!-- ... inside your StackLayout ... -->
<Entry Text="{Binding UserName, Mode=TwoWay}" Placeholder="Enter your name" />
<Label Text="Welcome, {Binding UserName}!" />
<!-- ... -->
Here, the Entry's Text property is bound to the UserName property of the ViewModel using Mode=TwoWay. Any text entered into the Entry will update the UserName property, and if the UserName property is changed programmatically, the Entry's text will also update.
Binding to Collections
Data binding is also used to display lists of data. You'll typically bind a CollectionView or ListView to an ObservableCollection in your ViewModel.
ViewModel Code (Example)
public class ItemViewModel : INotifyPropertyChanged
{
public string Name { get; set; }
// ... other properties
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); }
}
public class ListViewModel : INotifyPropertyChanged
{
private ObservableCollection<ItemViewModel> _items;
public ObservableCollection<ItemViewModel> Items
{
get => _items;
set
{
if (_items != value)
{
_items = value;
OnPropertyChanged(nameof(Items));
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); }
public ListViewModel()
{
Items = new ObservableCollection<ItemViewModel>
{
new ItemViewModel { Name = "Apple" },
new ItemViewModel { Name = "Banana" },
new ItemViewModel { Name = "Cherry" }
};
}
}
XAML View Code (Example)
<!-- ... inside your page content ... -->
<CollectionView ItemsSource="{Binding Items}">
<CollectionView.ItemTemplate>
<DataTemplate>
<Label Text="{Binding Name}" Padding="10" />
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
<!-- ... -->
The CollectionView's ItemsSource is bound to the Items ObservableCollection. The ItemTemplate defines how each item in the collection is displayed, binding to properties of the ItemViewModel.
Common Binding Issues & Tips
INotifyPropertyChanged and that you correctly call OnPropertyChanged whenever a bound property's value changes.
Null References
If a binding results in a null reference exception, double-check:
- The
BindingContextis set correctly. - The property names in your XAML match your ViewModel exactly (case-sensitive).
- The ViewModel properties are public.
Binding Converters
Sometimes you need to transform data before it's displayed or sent back to the ViewModel. Binding converters allow you to do this. For example, converting a boolean to a string representation or formatting a date.
Example: Using a Boolean to String Converter
ViewModel:
public bool IsActive { get; set; } = true;
Converter (Conceptual):
public class BooleanToStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is bool boolValue)
{
return boolValue ? "Active" : "Inactive";
}
return "N/A";
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is string stringValue)
{
return stringValue.Equals("Active", StringComparison.OrdinalIgnoreCase);
}
return false;
}
}
XAML (with converter registered and applied):
<!-- In your ResourceDictionary or Page.Resources -->
<local:BooleanToStringConverter x:Key="BoolConverter" />
<!-- In your UI -->
<Label Text="{Binding IsActive, Converter={StaticResource BoolConverter}}" />
Conclusion
Data binding is a powerful feature in .NET MAUI that, when used with the MVVM pattern, leads to cleaner, more maintainable, and testable applications. By understanding binding modes, collection binding, and converters, you can effectively manage data flow between your UI and your ViewModels.