Xamarin Forms - MVVM
The course is part of this learning path
This course looks at the Xamarin Forms implementation of the Model-View-ViewModel architecture, paying special attention to data and command binding. We see how to bind view models and nested models to views, and explore notification mechanisms built into the Xamarin Forms core for bi-directional data and view updating. The course also looks at XAML markup extensions and value converters for displaying images within a Xamarin Forms app.
Understand the model and view model aspects of MVVM and create an application using the Flyout app template
This course is designed for anyone interested in enhancing their knowledge of Xamarin, with a focus on MVVM.
To get the most from this course, you should have a basic understanding of interfaces and reflection in the context of C# and .NET.
In its most basic form, data binding connects or binds ViewModel properties, that is, public members to the view so that ViewModel data is displayed in the view. Data binding takes place within the context of a ViewModel. This context can be specified in the content page's code-behind file, as I've done here, instantiating a new StudentViewModel and assigning it to the page's BindingContext. Alternatively, you can define the binding context in the view's XAML with the content page binding context tag, referencing the view model via an XML namespace tag. While the page title isn't in the StudentViewModel class, it is a public member in the BaseViewModel class, a direct ancestor. However, the title which displays on the navigation bar in the basic flyout app isn't shown here due to the customized navigation bar. If you come from an MVC background, no naming convention associates viewmodels with views. The explicit assignment to the view's binding context makes the magic happen.
StudentViewModel maintains the student data in an instance of a student class. The student details label at the top of the page binds to the view model's FullName member. When StudentViewModel is first created, the student object is null, so the getter of FullName returns the text Student Details courtesy of an inline if statement. When a student exists, then FullName becomes the student's first name and last name concatenated using string interpolation.
The page title and FullName label bind data in one direction, from the ViewModel to the view, but as we've seen with the first and last name fields, it also flows from the view to the ViewModel. In the first name entry field, binding has several direction modes. Default, which is equivalent to not specifying a mode, will bind in a way appropriate to the control. Non-editable controls like a label will be one-way from the ViewModel source to the view. One time is also from the ViewModel source to the view, but only when the binding context changes, so a kind of set and forget, or more accurately, a one and done. You'd use this mode for static data. OneWayToSource sends data from the view to the ViewModel, and TwoWay allows data to flow in both directions.
Of course, data binding isn't magic, so it utilizes events and reflection to synchronize between the view and the data source. I say data source because, technically speaking, the source doesn't have to be a ViewModel. We could bind to the view's code-behind, but that wouldn't be MVVM, introducing bad design interdependencies, anyway, back to the view models.
In the flyout app template, the view models are descended from BaseViewModel, which implements the INotifyPropertyChanged interface, meaning all the view models share the functionality. Scrolling down to the bottom under the INotifyPropertyChanged region, we've got a public event and a protected method called OnPropertyChanged. If you were creating an app without a template, you would need to write this code yourself or something very similar. When a view model's property, so a public member, changes, you need to invoke PropertyChanged with the property's name. Behind the scenes, or more precisely, within System.ComponentModel .Net uses reflection, an internal introspection functionality, to search the source object for the property. Once found, the view is searched similarly for a control bound to the same property.
Back to the student view model, we can see the public properties mapped to the student model. FirstName returns the student's first name if the student object is not null, as denoted by the C# "?" operator via the getter function. When text is coming back from the view's first name entry field, the setter first sees if the student object is null and, if so, creates a new student. Do nothing if the student isn't null and the first name is the same as the incoming value. Otherwise, update the student object with the new first name value. Next, we fire off an OnPropertyChanged event for the student's first name. This isn't necessary from a data binding point of view but is required for the save button command – see how it's currently disabled. We'll be looking at command binding shortly. Next, an OnPropertyChanged is called for FullName to update the label at the top of the page.
As the picker control demonstrates, the view model allows us to bring together multiple models in one binding context. The item source is countries, a list of countries constructed from the list of country objects, while the selected item is bound to country derived from the student object.
So far, we have been creating a student. Let's see what happens if one already exists in the student store. We've got Jane Wilson from Greenland, who speaks UK English. Let's fire up the app, open the profile page, and see what happens. I'll put a breakpoint in LoadProfile, and when the page is created, we can see the student data has been successfully retrieved. However, Greenland isn't showing as the country when continuing the code execution. I suspect this is a timing issue, as all data except the country list is sourced locally within the app. Greenland is assigned as the countries selected item before the list is populated. I'll put a couple of breakpoints in the country property setter to see what's happening and run in debug mode again. The student's country is Greenland in the setter, but the new value coming in from the currently empty list is null. Null isn't the same as Greenland, so the student's country becomes null.
We need to fire country's OnPropertyChanged event after the list is populated and stop country's setter code from executing while the list is empty. I'll copy the OnPropertyChanged line and use the protected BaseModelView member IsBusy, set in the list loading routines, to test if the list has been populated. If this weren't a demo, I'd have a different busy flag for each list populating routine, as we can't determine which data source would return first. I'll fire OnPropertyChanged after IsBusy has been set to false. Ok, let's run that. We can see the delay in pulling the countries' data from the web service, but the student's country is correctly set once retrieved.
Some of you may have wondered why the view model's student member is public as its public members, FirstName, LastName, Country, and Language, are replicated in the view model. You're right, and it doesn't have to be in this scenario. But as it is, nothing stops you from binding the view's controls to the student object's properties, as we can see here. However, we will need the student class to implement the INotifyPropertyChanged interface. I'll start by commenting out the public members of StudentViewModel and Student. I've created private backing members for the four properties in the student class and pasted in the public members from the view model, including FullName. I've removed references to the student instance in the getters and setters and now reference the class's private members. I've also created a student implementation of PropertyChanged. These additions require me to reference System.ComponentModel and System.Runtime.CompilerServices in the class's using statements. I'll still need a flag variable to test if the view models lists are still loading, so I created a public bool called ViewModelIsLoading. In StudentViewModel's LoadCountries task, I need to set the student's ViewModelIsLoading flag, but only when the student isn't null. The call to OnPropertyChanged now needs to reference the view model's public student member instead of student.country. To get this to work properly, I also needed to place LoadProfile above LoadLists, so that student would not be null and student.ViewModelIsLoading is correctly set. Let's give that a whirl. Great, it works, but should I do it? I did it to show you referencing a complex object in the XAML binding, but it does require polluting your model with ViewModel functionality. As I said earlier, your data source doesn't have to be a view model; it just has to implement the INotifyPropertyChanged interface. I'd shy away from doing this as it introduces interdependencies between models and view models, but there may be situations where you need to do something along these lines.
Hallam is a software architect with over 20 years experience across a wide range of industries. He began his software career as a Delphi/Interbase disciple but changed his allegiance to Microsoft with its deep and broad ecosystem. While Hallam has designed and crafted custom software utilizing web, mobile and desktop technologies, good quality reliable data is the key to a successful solution. The challenge of quickly turning data into useful information for digestion by humans and machines has led Hallam to specialize in database design and process automation. Showing customers how leverage new technology to change and improve their business processes is one of the key drivers keeping Hallam coming back to the keyboard.