Exercise 2: Keeping the UI and data in sync using bindings (XAM270)
In this exercise you'll implement two-way binding for your UI; this will ensure UI data changes are sent to the code-behind. You'll then implement INotifyPropertyChanged
to ensure that the UI is updated when the underlying data is changed.
To complete the exercise, you will need Visual Studio for Windows or macOS with the Xamarin development tools installed. You will also need either an emulator/simulator or a device to run the exercise on. Please see the setup page if you need help installing the Xamarin development environment.
Open the starter solution
This exercise is a continuation of the previous exercise. You can use your existing solution or begin from the prior Exercise 1 > Completed solution in your copy of the cloned or downloaded course materials.
Add support for two-way bindings
Not all properties default to two-way binding so you'll set the binding mode explicitly.
- Open the MainPage.xaml markup file and on each
{Binding}
you created earlier, set theMode
to be "TwoWay". Remember you won't use quotes in the markup extension properties!
<Picker x:Name="country" SelectedItem="{Binding Country, Mode=TwoWay}" />
<Label Text="Current Design was Adopted on" FontAttributes="Bold" />
<DatePicker x:Name="adopted" MinimumDate="01/01/1700" HorizontalOptions="Start"
Date="{Binding DateAdopted, Mode=TwoWay}" />
<Label Text="Has Shield" FontAttributes="Bold" />
<Switch x:Name="hasShield" IsToggled="{Binding IncludesShield, Mode=TwoWay}" />
<Label Text="Fun Fact" FontAttributes="Bold" />
<Label x:Name="description" Text="{Binding Description}" />
Add support for property change notifications
In this part, you'll see why you need INotifyPropertyChanged
, and then implement it on our Flag
object.
- Open the MainPage.xaml.cs code behind file and locate the
OnShow
method. -
You want to modify your
Flag
object in some way that is independent of the UI.Add a year to the
Flag.DateAdopted
property by calling.AddYears(1)
and assigning the returning value back to theDateAdopted
property. Note: you must assign the property sinceDateTime
is immutable.private async void OnShow(object sender, EventArgs e) { CurrentFlag.DateAdopted = CurrentFlag.DateAdopted.AddYears(1); await DisplayAlert(CurrentFlag.Country, $"{CurrentFlag.DateAdopted:D} - {CurrentFlag.IncludesShield}: {CurrentFlag.MoreInformationUrl}", "OK"); }
- Run the application and note the current date displayed. Press the Show button in the tool bar. It should display the flag details but the year should be incremented in the alert dialog. Make note of the year displayed.
-
Dismiss the alert dialog and notice that the UI is still displaying the original year even though the data is changing in the underlying model.
The problem is that Xamarin.Forms is unaware of the property being changed - to make it aware, you need to implement the
INotifyPropertyChanged
interface on theFlag
class. - Open Flag.cs in the FlagData project.
-
Modify the
Flag
class to implementSystem.ComponentModel.INotifyPropertyChanged
. The implementation requires a single event namedPropertyChanged
.If you add the interface definition to the class, you can place your cursor on it and use ALT+ENTER (Windows) or CMD+ENTER (macOS) to get Visual Studio to provide a dummy implementation for you.
public class Flag : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; ... }
-
You'll create a helper-method to raise the
PropertyChanged
event namedRaisePropertyChanged
. The method will accept astring
parameter which is the text name of the property that has changed.- .NET 4.5 includes an attribute
CallerMemberNameAttribute
in theSystem.Runtime.CompilerServices
namespace which you can use to get the compiler to identify the property that has been changed. Check the code below for an example. Alternatively, you can have the caller pass the name using the C#nameof()
compiler feature. - Make sure to test the event for
null
before raising it. You can either use the built-in C# null check support, or for earlier versions of C#, test the event fornull
, or assign the event to an empty delegate (this is called the null pattern).
using System.Runtime.CompilerServices; ... public class Flag : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; ... private void RaisePropertyChanged([CallerMemberName] string propertyName = "") { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
- .NET 4.5 includes an attribute
-
You'll change the
DateAdopted
property first but you should perform these steps for every property which could be changed independent of the UI. -
Change the existing
DateAdopted
auto-property into a field-backed property and when it is changed, call the newRaisePropertyChanged
method to notify the UI.Hints:
- Make sure to compare the two values - it's inefficient to raise a property change notification if the value has not changed.
- Assign the new value.
- Raise the property change notification using your new method.
private DateTime _dateAdopted; ... public DateTime DateAdopted { get { return _dateAdopted; } set { if (_dateAdopted != value) { _dateAdopted = value; // Can pass the property name as a string, // -or- let the compiler do it because of the // CallerMemberNameAttribute on the RaisePropertyChanged method. RaisePropertyChanged(); } } }
-
Run the application, scroll down to the Current Design was Adopted On
DatePicker
and tab the Show button - you should see the date changing when it's changed in the underlyingFlag
object. You can fix the other properties if you like - you aren't changing any of them, but they would all need to raise thePropertyChanged
notification to properly notify the UI in a production application. The completed project included with the lab materials will take this extra step.- It can be helpful to create a method which does the field comparison and assignment so you only have to call that method in each property setter. Check the completed project for an example.
-
You can also remove the
x:Name
attributes off all the XAML objects you are now binding - this removes the private field mapped to the control and reduces the Intellisense "clutter" with unused objects.
Exercise summary
In this exercise, you have taken an existing Xamarin.Forms application and modified it to use Data Binding to populate the UI. You have successfully added two-way binding and implemented the INotifyPropertyChanged
interface on your model object.
You can view the completed solution in the Exercise 2 > Completed folder of your copy of the cloned or downloaded course materials.