Use data bindings in XAML
Data bindings can be declared in either code or in XAML using markup extensions. This unit discusses the latter as it's the most common way to create bindings. There are a couple of reasons to prefer XAML. First, most people consider bindings to be part of their UI code because the bindings get data for the UI to display. Second, there's a markup extension named Binding
that makes it easy to do.
What are data bindings
A binding ties two properties together. One property is in your UI and the other is in your data-model object. If the value of either property changes, the binding object can update the other one. In other words, bindings are intermediary objects that synchronize your UI and data. We use the terms source and target to identify the two objects involved:
Source: A source can be an object of any type. In practice, you typically use a data-object as your source. You need to identify the property on that source object to participate in the binding. You identify the property by setting the
Path
property in the binding.Target: The target is a property that is implemented using a special property called a
BindableProperty
. The object with theBindableProperty
must derive fromBindableObject
. All of the controls provided in .NET MAUI derive fromBindableObject
and most of their properties areBindableProperties
.
The following diagram illustrates how a binding is an intermediary object between two properties:
How to create a data binding in XAML
Let's look at a simple binding created in XAML by using the {Binding}
markup extension. It's binding the WeatherService.Humidity
property of the source to the Text
property of the UI control.
<VerticalStackLayout Margin="10">
<VerticalStackLayout.Resources>
<ResourceDictionary>
<services:WeatherService x:Key="myWeatherService" />
</ResourceDictionary>
</VerticalStackLayout.Resources>
<Label Text="{Binding Path=Humidity, Source={StaticResource myWeatherService}}" />
</VerticalStackLayout>
The binding source is:
An object instance of the
WeatherService
type. The instance is referenced through the{StaticResource ...}
XAML extension, which points to an object in the stack layout's resource dictionary.The
Path
points to a property namedHumidity
on theWeatherService
type.The
Path
is the first unnamed parameter on the{Binding}
syntax, and thePath=
syntax can be omitted. These two bindings are equivalent:<Label Text="{Binding Path=Humidity, Source={StaticResource myWeatherService}}" /> <Label Text="{Binding Humidity, Source={StaticResource myWeatherService}}" />
The binding target is:
- The
Label
control. - The control's
Text
property.
When the UI is displayed, the {Binding}
XAML extension creates a binding between the WeatherService
and Label
. The binding reads the WeatherService.Humidity
property's value into the Label.Text
property.
Use another control as a binding source
One useful feature of binding is being able to bind to other controls. The following XAML is a simple demonstration:
<VerticalStackLayout HorizontalOptions="Center" VerticalOptions="Center">
<Label x:Name="TargetLabel" Text="TEXT TO ROTATE" BackgroundColor="Yellow" />
<Slider WidthRequest="100" Maximum="360"
Value="{Binding Rotation, Mode=OneWayToSource, Source={x:Reference TargetLabel}}" />
</VerticalStackLayout>
The Slider.Value
property is bound to the Label.Rotation
property, but in a different way than previously explained. This property is using the binding mode OneWayToSource
, which reverses the typical binding mechanism. Instead of the Source updating the Target, OneWayToSource
updates the Source when the Target changes. In this example when the slider moves, it updates the rotation of the label based on the slider's value, as illustrated in the following animation:
The typical scenario for binding controls to one another is when a control, usually a collection control such as a ListView
or CarouselView
, has a selected item that you want to use as a data source. In the example of a page displaying the weather forecast, you might have a ListView
present a five-day forecast. When the user selects a day in the list, the details of that weather forecast are displayed in other controls. If the user selects another day, the other controls are again updated with the selected day's details.
Use the same source across multiple bindings
The previous example demonstrated using a static resource as a source for a single binding. That source can be used in multiple bindings. Here's an example of declaring a binding across three different controls, all binding to the same object and property Path
, though some omitting the Path
property:
<VerticalStackLayout Margin="10">
<VerticalStackLayout.Resources>
<vm:SimpleWeatherServiceObject x:Key="myWeatherService" />
</VerticalStackLayout.Resources>
<Entry Text="{Binding Humidity, Source={StaticResource myWeatherService}}" />
<Label Text="{Binding Path=Humidity, Source={StaticResource myWeatherService}}" />
</VerticalStackLayout>
You don't have to use the same Path
when using the same Source
:
<VerticalStackLayout Margin="10">
<VerticalStackLayout.Resources>
<vm:SimpleWeatherServiceObject x:Key="myWeatherService" />
</VerticalStackLayout.Resources>
<Entry Text="{Binding Temperature, Source={StaticResource myWeatherService}}" />
<Label Text="{Binding Path=Humidity, Source={StaticResource myWeatherService}}" />
</VerticalStackLayout>
Rarely do you present a single piece of data from a source, though it could happen. Usually you have several controls using different pieces of data from the same source. This situation is so common that the BindableObject
class has a property named BindingContext
that works as a source for data binding. Remember that .NET MAUI controls inherit from the BindableObject
class, so the .NET MAUI controls have the BindingContext
property.
Setting the Source
of the binding is optional. A binding that doesn't have Source
set automatically searches the XAML visual tree for a BindingContext
, which is set in the XAML or assigned to a parent element by code. Bindings are evaluated following this pattern:
If the binding defines a
Source
, that source is used, and searching stops. The binding'sPath
is applied to theSource
to get a value. IfSource
isn't set, the search begins for a binding source.The search begins with the target object itself. If the target object's
BindingContext
isn't null, the search stops and the binding'sPath
is applied to theBindingContext
to get a value. If theBindingContext
is null, then the search continues.This process continues until it reaches the XAML root. The search ends by checking the
BindingContext
of the root for a non-null value. If no validBindingContext
was found, the binding has nothing to bind against and does nothing.
It's common to set the BindingContext
at the root object's level, to apply to the entire XAML.
There's one last convenient feature that's worth mentioning. Bindings watch for changes to the object reference of their source. This works even for bindings that use BindingContext
as their source. If the Source
or BindingContext
is reassigned to another object, the bindings grab the data from the new source and update their target.