The Model-View-ViewModel Blueprint

Note

This eBook was published in the bound of 2017, and has not been updated since so. There is much in the book that remains valuable, only some of the textile is outdated.

The Xamarin.Forms developer experience typically involves creating a user interface in XAML, and and then adding code-behind that operates on the user interface. Every bit apps are modified, and grow in size and scope, complex maintenance issues can arise. These issues include the tight coupling between the UI controls and the business concern logic, which increases the price of making UI modifications, and the difficulty of unit testing such lawmaking.

The Model-View-ViewModel (MVVM) design helps to cleanly separate the business and presentation logic of an awarding from its user interface (UI). Maintaining a make clean separation between awarding logic and the UI helps to address numerous evolution issues and tin can make an application easier to test, maintain, and evolve. It can also greatly meliorate code re-utilize opportunities and allows developers and UI designers to more than easily collaborate when developing their respective parts of an app.

The MVVM Pattern

There are three core components in the MVVM pattern: the model, the view, and the view model. Each serves a distinct purpose. Figure 2-one shows the relationships between the iii components.

The MVVM pattern

Figure 2-1: The MVVM pattern

In add-on to understanding the responsibilities of each component, it's also important to understand how they interact with each other. At a high level, the view "knows well-nigh" the view model, and the view model "knows about" the model, only the model is unaware of the view model, and the view model is unaware of the view. Therefore, the view model isolates the view from the model, and allows the model to evolve independently of the view.

The benefits of using the MVVM pattern are as follows:

  • If in that location'south an existing model implementation that encapsulates existing business logic, information technology tin can exist hard or risky to modify it. In this scenario, the view model acts as an adapter for the model classes and enables y'all to avoid making any major changes to the model code.
  • Developers can create unit tests for the view model and the model, without using the view. The unit tests for the view model can practice exactly the same functionality equally used by the view.
  • The app UI can be redesigned without touching the lawmaking, provided that the view is implemented entirely in XAML. Therefore, a new version of the view should work with the existing view model.
  • Designers and developers tin work independently and concurrently on their components during the development process. Designers can focus on the view, while developers tin can work on the view model and model components.

The key to using MVVM finer lies in agreement how to factor app code into the correct classes, and in understanding how the classes interact. The following sections discuss the responsibilities of each of the classes in the MVVM pattern.

View

The view is responsible for defining the structure, layout, and appearance of what the user sees on screen. Ideally, each view is divers in XAML, with a limited code-backside that does not incorporate business logic. Notwithstanding, in some cases, the code-behind might contain UI logic that implements visual beliefs that is hard to limited in XAML, such as animations.

In a Xamarin.Forms awarding, a view is typically a Page-derived or ContentView-derived course. Withal, views tin as well exist represented by a data template, which specifies the UI elements to be used to visually represent an object when it's displayed. A data template equally a view does not have whatsoever code-behind, and is designed to demark to a specific view model type.

Tip

Avoid enabling and disabling UI elements in the code-behind. Ensure that view models are responsible for defining logical country changes that bear upon some aspects of the view'southward display, such as whether a command is bachelor, or an indication that an functioning is pending. Therefore, enable and disable UI elements past bounden to view model properties, rather than enabling and disabling them in lawmaking-behind.

There are several options for executing code on the view model in response to interactions on the view, such as a button click or particular selection. If a control supports commands, the control's Command property tin can be data-leap to an ICommand property on the view model. When the control's control is invoked, the code in the view model volition be executed. In add-on to commands, behaviors tin can be attached to an object in the view and tin can listen for either a control to be invoked or event to be raised. In response, the behavior can and so invoke an ICommand on the view model or a method on the view model.

ViewModel

The view model implements properties and commands to which the view can data bind to, and notifies the view of any state changes through modify notification events. The properties and commands that the view model provides define the functionality to be offered past the UI, simply the view determines how that functionality is to be displayed.

Tip

Proceed the UI responsive with asynchronous operations. Mobile apps should keep the UI thread unblocked to improve the user's perception of performance. Therefore, in the view model, use asynchronous methods for I/O operations and raise events to asynchronously notify views of property changes.

The view model is also responsible for coordinating the view'south interactions with any model classes that are required. There's typically a ane-to-many relationship betwixt the view model and the model classes. The view model might choose to betrayal model classes directly to the view so that controls in the view can information demark directly to them. In this example, the model classes will need to exist designed to support data binding and change notification events.

Each view model provides information from a model in a form that the view can hands consume. To attain this, the view model sometimes performs information conversion. Placing this data conversion in the view model is a good idea because it provides backdrop that the view can demark to. For example, the view model might combine the values of two backdrop to make it easier for display by the view.

Tip

Centralize data conversions in a conversion layer. Information technology's also possible to apply converters equally a divide information conversion layer that sits between the view model and the view. This tin can be necessary, for example, when data requires special formatting that the view model doesn't provide.

In lodge for the view model to participate in 2-way data binding with the view, its properties must raise the PropertyChanged outcome. View models satisfy this requirement by implementing the INotifyPropertyChanged interface, and raising the PropertyChanged event when a property is changed.

For collections, the view-friendly ObservableCollection<T> is provided. This collection implements collection changed notification, relieving the developer from having to implement the INotifyCollectionChanged interface on collections.

Model

Model classes are not-visual classes that encapsulate the app's data. Therefore, the model tin can be thought of as representing the app'southward domain model, which usually includes a data model forth with business concern and validation logic. Examples of model objects include data transfer objects (DTOs), Plain Sometime CLR Objects (POCOs), and generated entity and proxy objects.

Model classes are typically used in conjunction with services or repositories that encapsulate data admission and caching.

Connecting View Models to Views

View models can be continued to views past using the data-binding capabilities of Xamarin.Forms. In that location are many approaches that can be used to construct views and view models and associate them at runtime. These approaches fall into ii categories, known as view first composition, and view model commencement composition. Choosing betwixt view first composition and view model get-go composition is an issue of preference and complexity. Yet, all approaches share the aforementioned aim, which is for the view to have a view model assigned to its BindingContext property.

With view commencement composition the app is conceptually composed of views that connect to the view models they depend on. The main do good of this approach is that it makes it easy to construct loosely coupled, unit of measurement testable apps because the view models take no dependence on the views themselves. Information technology's also easy to understand the construction of the app by following its visual structure, rather than having to track code execution to understand how classes are created and associated. In addition, view start structure aligns with the Xamarin.Forms navigation arrangement that's responsible for constructing pages when navigation occurs, which makes a view model first limerick complex and misaligned with the platform.

With view model commencement composition the app is conceptually composed of view models, with a service being responsible for locating the view for a view model. View model get-go limerick feels more natural to some developers, since the view creation can be bathetic away, allowing them to focus on the logical not-UI structure of the app. In addition, information technology allows view models to be created past other view models. Still, this approach is often complex and information technology can become difficult to understand how the various parts of the app are created and associated.

Tip

Continue view models and views independent. The binding of views to a property in a information source should exist the view's chief dependency on its corresponding view model. Specifically, don't reference view types, such as Button and ListView, from view models. By post-obit the principles outlined here, view models can be tested in isolation, therefore reducing the likelihood of software defects by limiting telescopic.

The following sections talk over the principal approaches to connecting view models to views.

Creating a View Model Declaratively

The simplest approach is for the view to declaratively instantiate its corresponding view model in XAML. When the view is constructed, the corresponding view model object will likewise be constructed. This approach is demonstrated in the following code example:

              <ContentPage ... xmlns:local="clr-namespace:eShop">       <ContentPage.BindingContext>           <local:LoginViewModel />       </ContentPage.BindingContext>       ...   </ContentPage>                          

When the ContentPage is created, an instance of the LoginViewModel is automatically constructed and set as the view's BindingContext.

This declarative structure and consignment of the view model by the view has the advantage that it's simple, but has the disadvantage that it requires a default (parameter-less) constructor in the view model.

Creating a View Model Programmatically

A view can have code in the code-behind file that results in the view model being assigned to its BindingContext property. This is often accomplished in the view's constructor, as shown in the following code example:

              public LoginView()   {       InitializeComponent();       BindingContext = new LoginViewModel(navigationService);   }                          

The programmatic construction and assignment of the view model within the view'due south code-behind has the advantage that it's simple. All the same, the main disadvantage of this approach is that the view needs to provide the view model with any required dependencies. Using a dependency injection container can help to maintain loose coupling between the view and view model. For more information, see Dependency Injection.

Creating a View Divers as a Data Template

A view can exist defined equally a data template and associated with a view model type. Information templates can be defined as resource, or they can exist defined inline within the command that will display the view model. The content of the command is the view model example, and the data template is used to visually correspond information technology. This technique is an example of a situation in which the view model is instantiated outset, followed by the creation of the view.

Automatically Creating a View Model with a View Model Locator

A view model locator is a custom class that manages the instantiation of view models and their clan to views. In the eShopOnContainers mobile app, the ViewModelLocator grade has an attached holding, AutoWireViewModel, that's used to acquaintance view models with views. In the view's XAML, this attached property is set to true to indicate that the view model should be automatically continued to the view, as shown in the post-obit lawmaking case:

              viewModelBase:ViewModelLocator.AutoWireViewModel="true"                          

The AutoWireViewModel property is a bindable holding that'south initialized to fake, and when its value changes the OnAutoWireViewModelChanged outcome handler is chosen. This method resolves the view model for the view. The following lawmaking instance shows how this is achieved:

              individual static void OnAutoWireViewModelChanged(BindableObject bindable, object oldValue, object newValue)   {       var view = bindable equally Element;       if (view == goose egg)       {           return;       }        var viewType = view.GetType();       var viewName = viewType.FullName.Replace(".Views.", ".ViewModels.");       var viewAssemblyName = viewType.GetTypeInfo().Associates.FullName;       var viewModelName = string.Format(           CultureInfo.InvariantCulture, "{0}Model, {one}", viewName, viewAssemblyName);        var viewModelType = Type.GetType(viewModelName);       if (viewModelType == cypher)       {           return;       }       var viewModel = _container.Resolve(viewModelType);       view.BindingContext = viewModel;   }                          

The OnAutoWireViewModelChanged method attempts to resolve the view model using a convention-based arroyo. This convention assumes that:

  • View models are in the same assembly equally view types.
  • Views are in a .Views child namespace.
  • View models are in a .ViewModels child namespace.
  • View model names correspond with view names and end with "ViewModel".

Finally, the OnAutoWireViewModelChanged method sets the BindingContext of the view type to the resolved view model blazon. For more data about resolving the view model type, see Resolution.

This approach has the advantage that an app has a single class that is responsible for the instantiation of view models and their connexion to views.

Tip

Use a view model locator for ease of substitution. A view model locator tin besides be used equally a bespeak of substitution for alternate implementations of dependencies, such as for unit testing or pattern time information.

Updating Views in Response to Changes in the Underlying View Model or Model

All view model and model classes that are accessible to a view should implement the INotifyPropertyChanged interface. Implementing this interface in a view model or model form allows the class to provide change notifications to any data-bound controls in the view when the underlying property value changes.

Apps should exist architected for the correct employ of holding modify notification, by meeting the following requirements:

  • E'er raising a PropertyChanged effect if a public belongings's value changes. Practise not assume that raising the PropertyChanged event tin be ignored because of knowledge of how XAML binding occurs.
  • Always raising a PropertyChanged outcome for whatsoever calculated properties whose values are used by other properties in the view model or model.
  • Always raising the PropertyChanged event at the end of the method that makes a property alter, or when the object is known to be in a prophylactic state. Raising the event interrupts the performance past invoking the effect'south handlers synchronously. If this happens in the middle of an performance, information technology might expose the object to callback functions when it is in an unsafe, partially updated state. In addition, it'southward possible for cascading changes to exist triggered by PropertyChanged events. Cascading changes generally require updates to be complete earlier the cascading change is safe to execute.
  • Never raising a PropertyChanged event if the property does not change. This means that y'all must compare the onetime and new values before raising the PropertyChanged event.
  • Never raising the PropertyChanged event during a view model'southward constructor if yous are initializing a belongings. Data-bound controls in the view will not have subscribed to receive change notifications at this indicate.
  • Never raising more than i PropertyChanged effect with the same belongings name argument within a single synchronous invocation of a public method of a form. For instance, given a NumberOfItems belongings whose bankroll store is the _numberOfItems field, if a method increments _numberOfItems fifty times during the execution of a loop, it should just enhance property modify notification on the NumberOfItems holding in one case, later all the work is complete. For asynchronous methods, raise the PropertyChanged event for a given holding proper noun in each synchronous segment of an asynchronous continuation concatenation.

The eShopOnContainers mobile app uses the ExtendedBindableObject class to provide alter notifications, which is shown in the following code example:

              public abstract course ExtendedBindableObject : BindableObject   {       public void RaisePropertyChanged<T>(Expression<Func<T>> belongings)       {           var name = GetMemberInfo(property).Name;           OnPropertyChanged(proper noun);       }        private MemberInfo GetMemberInfo(Expression expression)       {           ...       }   }                          

Xamarin.Form's BindableObject class implements the INotifyPropertyChanged interface, and provides an OnPropertyChanged method. The ExtendedBindableObject course provides the RaisePropertyChanged method to invoke belongings modify notification, and in doing so uses the functionality provided by the BindableObject grade.

Each view model course in the eShopOnContainers mobile app derives from the ViewModelBase class, which in turn derives from the ExtendedBindableObject class. Therefore, each view model class uses the RaisePropertyChanged method in the ExtendedBindableObject grade to provide property change notification. The following code instance shows how the eShopOnContainers mobile app invokes property change notification by using a lambda expression:

              public bool IsLogin   {       go       {           return _isLogin;       }       set up       {           _isLogin = value;           RaisePropertyChanged(() => IsLogin);       }   }                          

Note that using a lambda expression in this way involves a small functioning toll considering the lambda expression has to exist evaluated for each call. Although the performance cost is minor and would not normally touch on an app, the costs tin accrue when in that location are many change notifications. Notwithstanding, the do good of this arroyo is that information technology provides compile-time type safety and refactoring back up when renaming properties.

UI Interaction using Commands and Behaviors

In mobile apps, deportment are typically invoked in response to a user action, such as a push click, that can be implemented by creating an event handler in the code-backside file. However, in the MVVM pattern, the responsibility for implementing the action lies with the view model, and placing code in the code-behind should be avoided.

Commands provide a convenient manner to stand for actions that can be bound to controls in the UI. They encapsulate the code that implements the action, and assist to go on information technology decoupled from its visual representation in the view. Xamarin.Forms includes controls that tin be declaratively continued to a control, and these controls will invoke the control when the user interacts with the control.

Behaviors besides allow controls to exist declaratively continued to a control. Yet, behaviors tin exist used to invoke an activity that's associated with a range of events raised by a control. Therefore, behaviors address many of the same scenarios as command-enabled controls, while providing a greater degree of flexibility and command. In improver, behaviors can as well be used to associate command objects or methods with controls that were non specifically designed to interact with commands.

Implementing Commands

View models typically expose control properties, for bounden from the view, that are object instances that implement the ICommand interface. A number of Xamarin.Forms controls provide a Command property, which can be data leap to an ICommand object provided by the view model. The ICommand interface defines an Execute method, which encapsulates the operation itself, a CanExecute method, which indicates whether the command can exist invoked, and a CanExecuteChanged event that occurs when changes occur that impact whether the command should execute. The Command and Control<T> classes, provided by Xamarin.Forms, implement the ICommand interface, where T is the type of the arguments to Execute and CanExecute.

Inside a view model, at that place should be an object of type Command or Control<T> for each public belongings in the view model of type ICommand. The Command or Command<T> constructor requires an Action callback object that's called when the ICommand.Execute method is invoked. The CanExecute method is an optional constructor parameter, and is a Func that returns a bool.

The post-obit code shows how a Control instance, which represents a register control, is constructed by specifying a delegate to the Register view model method:

              public ICommand RegisterCommand => new Control(Register);                          

The command is exposed to the view through a belongings that returns a reference to an ICommand. When the Execute method is chosen on the Control object, information technology but frontwards the telephone call to the method in the view model via the delegate that was specified in the Control constructor.

An asynchronous method can exist invoked by a command by using the async and wait keywords when specifying the command's Execute delegate. This indicates that the callback is a Task and should exist awaited. For example, the post-obit code shows how a Command instance, which represents a sign-in control, is constructed by specifying a delegate to the SignInAsync view model method:

              public ICommand SignInCommand => new Control(async () => look SignInAsync());                          

Parameters tin can be passed to the Execute and CanExecute actions by using the Command<T> course to instantiate the control. For example, the following lawmaking shows how a Command<T> case is used to indicate that the NavigateAsync method will crave an argument of type string:

              public ICommand NavigateCommand => new Command<string>(NavigateAsync);                          

In both the Control and Command<T> classes, the delegate to the CanExecute method in each constructor is optional. If a delegate isn't specified, the Command will return true for CanExecute. However, the view model can signal a change in the command's CanExecute condition by calling the ChangeCanExecute method on the Command object. This causes the CanExecuteChanged effect to be raised. Whatever controls in the UI that are leap to the command will then update their enabled status to reflect the availability of the data-jump command.

Invoking Commands from a View

The following code case shows how a Grid in the LoginView binds to the RegisterCommand in the LoginViewModel class by using a TapGestureRecognizer instance:

              <Filigree Filigree.Column="1" HorizontalOptions="Middle">       <Label Text="Annals" TextColor="Gray"/>       <Grid.GestureRecognizers>           <TapGestureRecognizer Command="{Binding RegisterCommand}" NumberOfTapsRequired="ane" />       </Filigree.GestureRecognizers>   </Grid>                          

A command parameter tin can likewise be optionally defined using the CommandParameter holding. The type of the expected argument is specified in the Execute and CanExecute target methods. The TapGestureRecognizer will automatically invoke the target command when the user interacts with the attached control. The command parameter, if provided, will be passed as the argument to the control'southward Execute consul.

Implementing Behaviors

Behaviors permit functionality to be added to UI controls without having to subclass them. Instead, the functionality is implemented in a behavior class and attached to the command as if it was part of the control itself. Behaviors enable you to implement code that you would normally have to write every bit code-backside, because it directly interacts with the API of the control, in such a way that information technology tin can be concisely attached to the control, and packaged for reuse beyond more than one view or app. In the context of MVVM, behaviors are a useful arroyo for connecting controls to commands.

A behavior that's attached to a control through fastened properties is known equally an attached behavior. The behavior tin can and then use the exposed API of the chemical element to which it is attached to add functionality to that command, or other controls, in the visual tree of the view. The eShopOnContainers mobile app contains the LineColorBehavior class, which is an fastened beliefs. For more than information near this behavior, see Displaying Validation Errors.

A Xamarin.Forms behavior is a class that derives from the Behavior or Behavior<T> class, where T is the blazon of the control to which the behavior should utilize. These classes provide OnAttachedTo and OnDetachingFrom methods, which should be overridden to provide logic that will exist executed when the behavior is fastened to and discrete from controls.

In the eShopOnContainers mobile app, the BindableBehavior<T> course derives from the Beliefs<T> class. The purpose of the BindableBehavior<T> class is to provide a base class for Xamarin.Forms behaviors that crave the BindingContext of the behavior to exist set to the fastened control.

The BindableBehavior<T> class provides an overridable OnAttachedTo method that sets the BindingContext of the behavior, and an overridable OnDetachingFrom method that cleans up the BindingContext. In addition, the form stores a reference to the fastened control in the AssociatedObject property.

The eShopOnContainers mobile app includes an EventToCommandBehavior class, which executes a command in response to an event occurring. This class derives from the BindableBehavior<T> class and then that the behavior can demark to and execute an ICommand specified by a Command property when the behavior is consumed. The following code example shows the EventToCommandBehavior class:

              public form EventToCommandBehavior : BindableBehavior<View>   {       ...       protected override void OnAttachedTo(View visualElement)       {           base of operations.OnAttachedTo(visualElement);            var events = AssociatedObject.GetType().GetRuntimeEvents().ToArray();           if (events.Any())           {               _eventInfo = events.FirstOrDefault(due east => e.Proper noun == EventName);               if (_eventInfo == cypher)                   throw new ArgumentException(string.Format(                           "EventToCommand: Can't find any event named '{0}' on fastened blazon",                            EventName));                AddEventHandler(_eventInfo, AssociatedObject, OnFired);           }       }        protected override void OnDetachingFrom(View view)       {           if (_handler != nothing)               _eventInfo.RemoveEventHandler(AssociatedObject, _handler);            base.OnDetachingFrom(view);       }        individual void AddEventHandler(               EventInfo eventInfo, object detail, Activeness<object, EventArgs> activeness)       {           ...       }        private void OnFired(object sender, EventArgs eventArgs)       {           ...       }   }                          

The OnAttachedTo and OnDetachingFrom methods are used to register and deregister an event handler for the issue divers in the EventName belongings. Then, when the result fires, the OnFired method is invoked, which executes the command.

The advantage of using the EventToCommandBehavior to execute a control when an event fires, is that commands can exist associated with controls that weren't designed to interact with commands. In improver, this moves consequence-handling code to view models, where it can be unit tested.

Invoking Behaviors from a View

The EventToCommandBehavior is particularly useful for attaching a control to a control that doesn't support commands. For instance, the ProfileView uses the EventToCommandBehavior to execute the OrderDetailCommand when the ItemTapped event fires on the ListView that lists the user's orders, as shown in the post-obit code:

              <ListView>       <ListView.Behaviors>           <behaviors:EventToCommandBehavior                          EventName="ItemTapped"               Command="{Bounden OrderDetailCommand}"               EventArgsConverter="{StaticResource ItemTappedEventArgsConverter}" />       </ListView.Behaviors>       ...   </ListView>                          

At runtime, the EventToCommandBehavior will respond to interaction with the ListView. When an item is selected in the ListView, the ItemTapped upshot will fire, which will execute the OrderDetailCommand in the ProfileViewModel. By default, the event arguments for the event are passed to the command. This data is converted as it's passed between source and target by the converter specified in the EventArgsConverter holding, which returns the Item of the ListView from the ItemTappedEventArgs. Therefore, when the OrderDetailCommand is executed, the selected Order is passed every bit a parameter to the registered Activity.

For more information virtually behaviors, run across Behaviors.

Summary

The Model-View-ViewModel (MVVM) pattern helps to cleanly separate the business and presentation logic of an awarding from its user interface (UI). Maintaining a make clean separation between awarding logic and the UI helps to address numerous development bug and tin make an application easier to test, maintain, and evolve. It tin can also greatly ameliorate lawmaking re-use opportunities and allows developers and UI designers to more easily collaborate when developing their corresponding parts of an app.

Using the MVVM pattern, the UI of the app and the underlying presentation and business logic is separated into three separate classes: the view, which encapsulates the UI and UI logic; the view model, which encapsulates presentation logic and state; and the model, which encapsulates the app's business logic and data.

  • Download eBook (2Mb PDF)
  • eShopOnContainers (GitHub) (sample)