MAUI Data Binding


In .NET MAUI, data binding allows us to connect UI elements to data sources for the data to be automatically bound when changes made on the source are reflected on the UI. It’s very important when you’re creating complex applications as it makes your UI much more dynamic and easy to manage.

Let’s build a simple application where users can enter information about tasks and be sorted into view a list of all tasks. We will see one way and two way data binding in this example.

Creating the ViewModel

The UI will bind to data, and the ViewModel (updated below) will have properties containing that data. Let’s hook the INotifyPropertyChanged so that the UI is updated when the properties change.

using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace MauiAppExample
{
    public class TaskViewModel : INotifyPropertyChanged
    {
        private string _taskName;
        private string _taskDescription;

        public string TaskName
        {
            get => _taskName;
            set
            {
                if (_taskName != value)
                {
                    _taskName = value;
                    OnPropertyChanged();
                }
            }
        }

        public string TaskDescription
        {
            get => _taskDescription;
            set
            {
                if (_taskDescription != value)
                {
                    _taskDescription = value;
                    OnPropertyChanged();
                }
            }
        }

        public ObservableCollection<string> Tasks { get; set; }

        public Command AddTaskCommand { get; }

        public TaskViewModel()
        {
            Tasks = new ObservableCollection<string>();
            AddTaskCommand = new Command(AddTask);
        }

        private void AddTask()
        {
            if (!string.IsNullOrWhiteSpace(TaskName))
            {
                Tasks.Add($"{TaskName}: {TaskDescription}");
                TaskName = string.Empty;  // Clear the inputs
                TaskDescription = string.Empty;
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}
  • INotifyPropertyChanged: While it allows updating UI elements on a property change.
  • Properties (TaskName, TaskDescription): These are two way data bound to the UI they are also data bound to the UI so that user input can update them.
  • ObservableCollection (Tasks): Also makes it perfect for list binding as it will automatically notify the UI when items are added or removed.
  • Command (AddTaskCommand): Added to the list, invoked to make a new task appear.

Defining the User Interface

But for user input, the UI will use Entry elements, and for showing the list of tasks will use a ListView. Throughout the UI, data binding will be used.

<?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"
             x:Class="MauiAppExample.MainPage"
             xmlns:local="clr-namespace:MauiAppExample">

    <ContentPage.BindingContext>
        <local:TaskViewModel />
    </ContentPage.BindingContext>

    <StackLayout Padding="20" Spacing="20">

        <Label Text="Add a New Task" FontSize="Large" HorizontalOptions="Center" />

        <!-- Entry for Task Name -->
        <Entry Placeholder="Task Name"
               Text="{Binding TaskName, Mode=TwoWay}" />

        <!-- Entry for Task Description -->
        <Entry Placeholder="Task Description"
               Text="{Binding TaskDescription, Mode=TwoWay}" />

        <!-- Button to Add Task -->
        <Button Text="Add Task"
                Command="{Binding AddTaskCommand}" />

        <!-- ListView to display tasks -->
        <ListView ItemsSource="{Binding Tasks}">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <TextCell Text="{Binding}" />
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>

    </StackLayout>
</ContentPage>
  • ContentPage.BindingContext: In this case, it sets the TaskViewModel as the page’s data context and therefore data binding can be done between the View and ViewModel.
  • Entry Controls: Make TaskName and TaskDescription properties to be bound to with two way (Mode=TwoWay) binding.
  • Button: When clicked, it binds that AddTaskCommand to add tasks.
  • ListView: Displays the list of tasks with bind to the Tasks collection.

MainPage Code-Behind

Most of the logic is actually in the ViewModel, so the MainPage.xaml.cs is just as simple as you would expect it to be.

namespace MauiAppExample
{
    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();
        }
    }
}
  • MainPage Constructor: The page is initalised by calling InitializeComponent() first.
  • As its name implies, the data binding logic is encapsulated in XAML alone, so we don’t need any additional code within.

Data Binding in This Example

  • Two-Way Data Binding:
    • The properties (TaskName, TaskDescription) bound to this Entry controls have two way binding. This means that if we change the Entry, it will also change our ViewModel, and vice versa.
  • Commands:
    • Users can ironically invoke this command (the AddTaskCommand) by binding the Button control to the AddTaskCommand in the ViewModel.
  • ObservableCollection:
    • One ObservableCollection, named Tasks, is bound to a ListView. Whenever a new task is added, the UI is updated automatically.

Key Concepts for Data Binding

ConceptExplanation
BindingContextSets the data context for the page or control.
INotifyPropertyChangedNotifies the UI when a property value changes.
Two-Way BindingUpdates the source property when the UI changes, and vice versa.
ObservableCollectionAutomatically notifies the UI of changes in a collection.
CommandsEncapsulates a method that can be bound to a UI element (e.g., button).

Summary

  • A ViewModel reduces the UI logic outside of the UI element..
  • Data binding automatically keeps the UI in sync with underlying data, which somehow contributes to improving maintainabilty and reducing the manual updates.
  • Adding a new task is a case where you can use commands to cleanly handle a user interaction.

With data binding in .NET MAUI, you can easily build responsive and maintainable applications by keeping data apart from your UI and automatically synchronizing changes.