Using the MVVM pattern with a Xamarin Forms mobile app

Image for post
Image for post

This article assumes that the reader is already familair with the MVVM software design pattern. If you are not familair with this design pattern, then it’s worth reading up on that first, before proceeding with this article. There are many descriptions of this article, including this one[^]. It is useful to understand the design pattern from a purely conceptual perspective, before looking at the various technical impementations of it. By understanding the design pattern at a conceptual level, you will far easier comprehend its implementation details.

I have used the MVVM design pattern previously. In fact, I have used the MVVM pattern within our current mobile app. For this, I used Kendo UI controls in conjunction with javascript. This particular implementation uses what is known as an observable. An observable (which is based on the Observer design pattern[^] is an object that maintains a list of dependents (called observers) and notifies them of any changes in state. It is this notification system that provides the two-way notification (or binding) that is essential to the MVVM design pattern.

With our latest incarnation of the mobile app now well underway, we have come to the point where we can start building our data entry forms. I have so far implemented the underpinning infrastructure and architecture which enables the app to consume our services, save data to local storage using SQL Lite and send emails from the app. All of this is now fully implemented and working.

We have several data entry forms within our app that allow the user to submit data to our backend services. These include forms for submitting:

- mileages
- service, repair and MOT bookings
- vehicle inspections

As we have already done so in our previous mobile app, we will be using the MVVM design pattern to implement these data entry forms.

We will impement the data entry forms using XAML and Telerik controls. We could have used the native Xamarin UI controls, but there is a greater selection of Telerik controls, and they provide a consistent API and are easily themable. Although the implementation uses Telerik controls and XAML, the underlying concepts can be applied with any UI technology.

I’ll use an example that refers to a simple data entry form that allows a user to enter a message which is sent to the backend service. The message may be to request information for example. This trivial example containing just the one UI control should suffice to demonstrate how the MVVM pattern can be implemented.

I tend to begin the development of a new data entry form from the Model and work backwards from there i.e. Model -> ViewModel -> View.

All Models inherit from the the same base Model class. This base Model class inherits from NotifyPropertyChangedBase which is a Telerik class that supports behaviour similar to INotifyPropertyChanged.

Hide Copy Code

public class BaseFormModel : NotifyPropertyChangedBase
{
}

This ensures that all Models used by the data entry forms will support the ability to raise events when a property on the Modelchanges. These changes to the Model will be notified to the ViewModel.

Models used by the data entry forms also inherit from the following interface.

Hide Copy Code

public interface IFormData<T>
{
T CreateDefaultModel();
}

By implementing this interface, the Model must therefore contain the method CreateDefaultModel(). This method is used by the ViewModel to supply a default Model (containing default values) which can be used when the View (the XAML form) is first displayed to the user. It implements generics which therefore allows it to work with any type of Model.

Here’s the Model for the “Message Us” data entry form. For the purposes of this simple example I have removed much of the code for clarity.

Hide Copy Code

public class MessageUsModel : BaseFormModel, IFormData<MessageUsModel>
{
private string _messageToSend;
[DisplayOptions(Header = MessageUsModelConstants.MessageHeader)]
[NonEmptyValidator(MessageUsModelConstants.MessageError)]
public string MessageToSend
{
get => _messageToSend;
set
{
if (_messageToSend == value) return;
_messageToSend = value;
OnPropertyChanged();
}
}
public MessageUsModel CreateDefaultModel()
{
return new MessageUsModel
{
_messageToSend = ""
};
}
}

The decorations on the public property MessageToSend are Telerik specific and define the validation rules / messages for the property. These rules / messages are then enforced by the View. Using this particular implementation of MVVM, the data rules are therefore defined at the level of the Model (which makes sense). Whenever a new value is set on the MessageToSend property, the OnPropertyChanged() event is raised. This updates the state of the ViewModel that is bound to the Model.

Moving onto the ViewModel, we define the base behaviour for all our ViewModels in our base class.

Hide Copy Code

public abstract class ViewModelBase<T> : NotifyPropertyChangedBase where T : new()
{
/// <summary>
/// This is the View-Model's reference to the Model. Use this whenever the view-model needs to reference the model.
/// </summary>
public T FormModel = new T();
/// <summary>
/// Implement this method in the derived class to define what actions to take when the user
/// submits data from one of the data-entry forms
/// </summary>
/// <returns></returns>
public abstract Task PostCompleteTask();
}

I have used an abstract class that inherits from the same Telerik class as the base Model class i.e. NotifyPropertyChangedBase. The public property FormModel is a reference to the Model. This property is used by the ViewModel when it needs to refer to the Model. The method PostCompleteTask() is invoked by the ViewModel when the form is ready to be submitted. As this is an abstract method, it must therefore be implemented by each inheriting subclass. This provides consistency to all of our ViewModels. The actual work performed by each ViewModel will always be defined within this method.

Here’s the ViewModel for the “Message Us” class. For the purposes of this simple example I have removed much of the code for clarity.

Hide Copy Code

public class MessageUsViewModel : ViewModelBase<MessageUsModel>
{
public MessageUsModel MessageUsModel;

public MessageUsViewModel()
{
this.MessageUsModel = this.FormModel.CreateDefaultModel();
}
public override async Task PostCompleteTask()
{
//implement your code here to submit the form data
}
}

The public property MessageUsModel is the reference to our Model. This is initially populated with a default instance in the class constructor by invoking the method CreateDefaultModel() (which we saw earlier) using the public property FormModel (which we also saw earlier).

Hide Copy Code

this.MessageUsModel = this.FormModel.CreateDefaultModel();

When the user has finished entering their message and is ready to submit the form, clicking on the form’s submit button will invoke the PostCompleteTask() method that will perform whatever processing as necessary (in our case all form data is submitted to our backend services using RESTful Web API services).

Finally, here’s the XAML for the View and the code-behind.

Hide Expand

Copy Code

[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class MessageUsView : ContentPage
{
public MessageUsViewModel Muvm;
public MessageUsView()
{
InitializeComponent();
//initiate the view-model and bind it to the form
this.Muvm = new MessageUsViewModel();
this.BindingContext = this.Muvm;
}
private async void DataFormValidationCompleted(object sender, FormValidationCompletedEventArgs e)
{
dataForm.FormValidationCompleted -= this.DataFormValidationCompleted;
if (e.IsValid)
{
await this.Muvm.PostCompleteTask();
}
}
private void CommitButtonClicked(object sender, EventArgs e)
{
dataForm.FormValidationCompleted += this.DataFormValidationCompleted;
dataForm.CommitAll();
}
}

And the XAML code.

Hide Copy Code

<input:RadDataForm x:Name="dataForm" CommitMode="Immediate"  />
<input:RadButton x:Name="CommitButton" Text="Save" Clicked="CommitButtonClicked" IsEnabled="True"/>

The important parts to note are the setting up of the binding between the View and the ViewModel in the constructor. This sets up the two-way binding, such that any changes in the View are reflected in the ViewModel and vice-versa. These changes are also reflected in the underlying Model (if that wasn’t already clear).

Hide Copy Code

this.Muvm = new MessageUsViewModel();
this.BindingContext = this.Muvm;

When the user clicks the Submit button, the actions implemented within the ViewModel’s PostCompleteTask() method are invoked.

This is a fairly simple example. In a real world use case there would undoubtedly be more complexity, but this should serve as a useful example of using the MVVM design pattern within a Xamarin mobile app. The fact that we are using Telerik UI controls doesn’t change the core concepts discussed. The MVVM design pattern is a very powerful design pattern that is perfect for use within data entry forms.

A father, cyclist, vegetarian, atheist, geek and multiple award winning technical author. Loves real ale, fine wine and good music. All round decent chap.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store