Xamarin.Forms Fundamentals: MVVM

In my previous post we started with a blank Xamarin.Forms app and added manual data-binding.   That is, when a button was pressed, we assigned the value in an entry control to the text property of a label. 

Yuck.

Today we’ll look at a better way of doing that.  But hold on to your hat, and fasten your seat belt, because we’re going to dive into the deep end.   

You may want to create a new Blank Xamarin.Forms app, as there will be a lot of new material in this example.  Once your app is created, add three folders to the solution:  Model, View and ViewModel.

You already have a MainPage.xaml.  Drag it up into the View folder.  Add a label and a button. 

<Label
    Text="{Binding WelcomeText}" 
    HorizontalOptions="Center"
    VerticalOptions="Center" />

<Button Text="Change" Command="{Binding ButtonClickedCommand}" />

Notice that the Text property of the label says that it is bound to WelcomeText.  WelcomeText will be the name of a property in the corresponding ViewModel. 

By convention, the ViewModel class will have the same name as the View class, but with the ending ViewModel appended.  Thus, the View is MainPage and the ViewModel is MainPageViewModel.

Data Binding

By binding the control to a property, we tell Xamarin.Forms: “When this property changes, update this control.” 

Let’s create a new class in the ViewModel folder named MainPageViewModel.cs, and add the property we want to bind to.

private string welcomeText = "Hello world"; 
public string WelcomeText
 {
    get => welcomeText;
    set => SetValue( ref welcomeText, value );
 }

There is a lot to see here.  WelcomeText is a property and thus has a setter and a getter. 

In order for databinding to work you need four ingredients:

  • The Binding keyword in the XAML
  • A BindingContext
  • The property to bind to
  • Implementation of INotifyPropertyChanged

We’ve seen the binding keyword.

The BindingContext is just a way to tell Xamarin.Forms where to find the properties you are binding to.  In this case, the BindingContext is MainPageViewModel, which we indicate in MainPage.xaml.cs

 public MainPage()
 {
    InitializeComponent();
    var vm = new MainPageViewModel();
    BindingContext = vm;
 }

INotifyPropertyChanged

The property to bind to is in the ViewModel (as we saw) and then we need to implement INotifyPropertyChanged.  This interface is implemented in each of your ViewModel classes, which becomes tedious, so we move it up to the base view model. 

The base view model I like does a little fancy footwork: it creates the SetValue method to make binding even easier.  Here is the base view model:

 public abstract class BaseViewModel : INotifyPropertyChanged
 {
    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged( 
           [CallerMemberName] string propertyName = null )
    {
       PropertyChanged?.Invoke( 
               this, new PropertyChangedEventArgs( propertyName ) );
    }

    protected void SetValue<T>( ref T   backingField,
          T       value,
          [CallerMemberName] string propertyName = null )
    {
       if (EqualityComparer<T>.Default.Equals( 
                    backingField, value )) return;
       backingField = value;
       OnPropertyChanged( propertyName );
    }
}

The first line declares the (required) event PropertyChanged, which is defined by the interface.  

Next we have a pretty standard handler for that event named OnPropertyChanged.  This checks to see if anyone has registered with the event, and if so it fires off the event with the name of the property that was updated.

Finally, we have SetValue.  This is a helper method designed to make setting the property easier.  For now, you can just take this one on faith, or if you are comfortable with generics you can study it for a bit to see how it works.  

Binding at work

Let’s trace the binding.  If something (say the button handler) sets the WelcomeText value in the ViewModel, The underlying backing variable (welcomeText) will be updated and NotifyPropertyChanged will be raised.  Since the view has registered for that (by setting the binding context), the control bound to that property (the label) will be updated.  Magic.

Commands

But wait! There’s more!

In the previous example, we used an event handler for the button, and handled the event in code behind.  This is icky because it is hard to write unit tests against views.  So we want to move the event handling to the View Model.  To do that, we use a Command,

 <Button Text="Change" Command="{Binding ButtonClickedCommand}"

A command is like a property, in that you can bind to it, as we have done here.  In the view model, we declare the command as a property,

public ICommand ButtonClickedCommand { get; set; }

and then, in the View Model constructor, we instantiate the command, providing it the name of the method that will handle the command.

public MainPageViewModel()
{
   ButtonClickedCommand = new Command(OnButtonClicked);
}

This says that when the button is clicked, the command will fire and the method OnButtonClicked will  be called.  

private void OnButtonClicked()
{
   WelcomeText = "Hello Bound Command!!";
}

Notice that in OnButtonClicked we set the text for the WelcomeText property.  We do not set the label’s text directly.  By setting the WelcomeText property, the NotifyChanged property is fired and the label rebinds its text to the text in the property.  Boom! that text is displayed.

That’s a lot of moving parts, so read this all again.  We’ll continue building out this app in coming blog posts.

About Jesse Liberty

Jesse Liberty has three decades of experience writing and delivering software projects and is the author of 2 dozen books and a couple dozen online courses. His latest book, Building APIs with .NET will be released early in 2025. Liberty is a Senior SW Engineer for CNH and he was a Senior Technical Evangelist for Microsoft, a Distinguished Software Engineer for AT&T, a VP for Information Services for Citibank and a Software Architect for PBS. He is a Microsoft MVP.
This entry was posted in Essentials. Bookmark the permalink.