Model-View-ViewModel (MVVM) implementation
In response to the tight-coupling problem, another derivative of MVC was born with the release of the Windows Presentation Foundation (WPF). The idea that was coined by Microsoft was the concept of outlets being exposed by a controller (or ViewModel, in this case) and the outlets being coupled by the view elements. The concept of these outlets and their coupling is called the binding:
Using bindings, we can decrease the amount of knowledge of the view-model about the inner workings of the view elements and let the application runtime handle the synchronization on the outlets on View and ViewModel fronts.
In addition to bindings, the concept of command becomes invaluable so that we can delegate the user actions to the ViewModel. A command is a stateful single execution unit that represents a function and the related data to execute this function.
Using our previous example, we can create a view-model to demonstrate the benefits of using MVVM:
- Let's start by creating a class that represents the user interaction points on our view (that is, two string fields for username and password, and two functions for login and signup):
public class LoginViewModel
{
private string _userName;
private string _password;
private string _result;
public LoginViewModel()
{
}
public string UserName
{
get
{
return _userName;
}
set
{
if (_userName != value)
{
_userName = value;
}
}
}
public string Password
{
get
{
return _password;
}
set
{
if (_password != value)
{
_password = value;
}
}
}
public string Result
{
get
{
return _result;
}
set
{
if (_result != value)
{
_result = value;
}
}
}
public void Login()
{
//TODO: Login
Result = "Successfully Logged In!";
}
public void Submit()
{
// TODO:
}
}
- At this stage of implementation, we can bind the Entry fields from our view to the view-model. In order to assign the view-model to the view, use the BindingContext of our view:
public partial class LoginView : ContentPage
{
public LoginView()
{
InitializeComponent();
BindingContext = new LoginViewModel();
}
}
- We can now set up the bindings for the Entry fields:
<Label Text="Username" />
<Entry x:Name="usernameEntry" Placeholder="username" Text="{Binding UserName}" />
<Label Text="Password" />
<Entry x:Name="passwordEntry" IsPassword="true" Placeholder="password" Text="{Binding Password}" />
<Button x:Name="loginButton" Text="Login" />
<Label x:Name="messageLabel" Text="{Binding Result}" />
When executing this sample, you will notice that the values for the entries with unidirectional data flow (that is, the UserName and Password fields are only propagated from the View to the view-model) are behaving as expected; the values that are entered in the associated fields are pushed to the properties, as expected.
In order to increase the binding's performance and decrease the resources that are used for a certain binding, it is important to define the direction for the binding. There are various BindingMode available, as follows:
- OneWay: This is used when the ViewModel updates a value. It should be reflected on the view.
- OneWayToSource: This is used when the view changes a value. The value change should be pushed to the view-model.
- TwoWay: The data flow is bi-directional.
- OneTime: The synchronization of data occurs only once when the binding context is bound, and data is propagated from the view-model to the view.
With this information at hand, the username and password fields should be using the OneWayToSource binding, whereas the message label should use a OneWay binding mode, since the result is only updated by the view-model.
The next step would be to set up the commands for the functions to be executed (that is, login and signup). Semantically, a command is composed of a method (with its enclosed data and/or arguments) and a state (whether it can be executed or not). This structure is described by the ICommand interface:
public interface ICommand
{
void Execute(object arg);
bool CanExecute(object arg);
event EventHandler CanExecuteChanged;
}
In Xamarin.Forms, there are two implementations of this interface: Command and Command<T>. Using either of these classes, command bindings can be accomplished. For instance, in order to expose the Login method as a command, follow these steps:
- First, declare our Command property:
private Command _loginCommand;
public ICommand LoginCommand { get { return _loginCommand; } }
- In order to initialize _loginCommand, use the constructor:
public LoginViewModel()
{
_loginCommand = new Command(Login, Validate);
}
Note that we used two actions to initialize the command. The first action is the actual method execution, while the second function is a method that returns a Boolean indicating whether the method can be executed.
- The Validate method's implementation could look like this:
public bool Validate()
{
return !string.IsNullOrEmpty(UserName) && !string.IsNullOrEmpty(Password);
}
- Finally, in order to complete the implementation, send the CanExecuteChanged event whenever the UserName or Password fields are changed:
public string UserName
{
get
{
return _userName;
}
set
{
if (_userName != value)
{
_userName = value;
_loginCommand.ChangeCanExecute();
}
}
}
public string Password
{
get
{
return _password;
}
set
{
if (_password != value)
{
_password = value;
_loginCommand.ChangeCanExecute();
}
}
}
- Now, if we were to run the application, you would see how the disabled and enabled states of the command are reflected on the UI.
The same setup can be used with methods that require an input argument using the Command<T> class:
- Once the command setup is complete, we only have the result message binding, which is still not working as expected. At this point, tapping the login button will update the view-model data, and yet the user interface will not reflect this data change.The reason for this is the fact that this field should be bound with a OneWay binding (changes in the source should be reflected on the target) and the main requirement for this is that the source (view-model) should be implementing the INotifyPropertyChanged interface. INotifyPropertyChanged is the essential mechanism for propagating the changes on the binding context to the view elements:
/// <summary>Notifies clients that a property value has changed.</summary>
public interface INotifyPropertyChanged
{
/// <summary>Occurs when a property value changes.</summary>
event PropertyChangedEventHandler PropertyChanged;
}
A simple implementation would require the invocation of the PropertyChanged event with the property that is currently being changed.
- Finally, by including the event trigger on the setter of the Result property, we should able to see the outcome of the Login command:
public string Result
{
get
{
return _result;
}
set
{
if (_result != value)
{
_result = value;
PropertyChanged?.Invoke(this, new
PropertyChangedEventArgs(nameof(Result))); ;
}
}
}
This finalizes the view and view-model setup for the login page. In this example, we have created a setup where the view is responsible for creating the view-model; however, by using an implementation of Inversion of Control (IoC), such as dependency injection or the service locator pattern, the view can be dismissed of this duty.