Wednesday, 13 May 2015

Creating a flexible and maintainable Xamarin.Forms app with Prism

Prism, a set of libraries developed by Microsoft, helps you to design and build apps using loosely-coupled components that can evolve independently but that can be easily integrated into the overall app. Prism now includes support for Xamarin.Forms in the form of a preview library. This library helps to produce Xamarin.Forms apps that are flexible, maintainable, and testable.

Prism is designed to help developers create apps that need to accomplish the following:

  • Address common app development scenarios.
  • Separate the concerns of presentation, presentation logic, and model through support for the Model-View-ViewModel (MVVM) pattern.
  • Use an architectural infrastructure to produce a consistent and high quality app.

The logical architecture of a typical Xamarin.Forms app that uses Prism is shown in the following diagram. Grey items are provided by Prism, with blues items having to be created by the developer.

image

In this blog post I’ll take the sample app developed in my previous blog post, and convert it to use Prism in order to further the separation of concerns in the sample app. I’ll assume that you are familiar with the Unity dependency injection container. However, you are not required to use Unity, or any other dependency injection container, in order to use Prism.

Implementation

Bootstrapping the app

The App class, in the XamarinPhotoViewer project, has the responsibility for bootstrapping the app.

1 public class App : Application
2 {
3 public App()
4 {
5 var bootstrapper = new Bootstrapper();
6 bootstrapper.Run(this);
7 }
8 }

This is achieved by using the App constructor to create an instance of the Bootstrapper class, and run it. The Bootstrapper class is simply a class that initializes the services that Prism will be using.

1 public class Bootstrapper : UnityBootstrapper
2 {
3 protected override Page CreateMainPage()
4 {
5 return Container.Resolve<MainPage>();
6 }
7
8 protected override void RegisterTypes()
9 {
10 Container.RegisterTypeForNavigation<PhotoPage>();
11 }
12 }

The Bootstrapper class derives from the UnityBootstrapper class, which is a Prism provided class that handles the initialization of the Unity container. The Bootstrapper class overrides the CreateMainPage method in order to return the page to be used as the root page of the app, and overrides the RegisterTypes method in order to register the PhotoPage view so that it can be used in navigation.

Connecting view models to views

Prism provides a view model locator object that is responsible for the instantiation of view models and their association to views. This has the advantage that the app has a single class that is responsible for the instantiation of view models.

The ViewModelLocator class has an attached property, AutowireViewModel that is used to associate 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 connected to the view.

1 <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
2 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
3 xmlns:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
4 prism:ViewModelLocator.AutowireViewModel="true"
5 x:Class="XamarinPhotoViewer.Views.MainPage">
6 ...
7 </ContentPage>

The ViewModelLocator object uses a convention-based approach to locate and instantiate view models from views. This convention assumes that view models are in the same assembly as the view types, that view models are in a .ViewModels child namespace, that views are in a .Views child namespace, and that view model names correspond with view names and end with “ViewModel”.

Updating a view in response to changes in the view model

All view model classes that are accessible to the view should implement the INotifyPropertyChanged interface. This allows view models to provide change notifications to any data-bound controls in the view when the underlying property value changes. However, this can be repetitive and error prone. Therefore, Prism provides the BindableBase class that implements the INotifyPropertyChanged interface.

Each view model class in the sample app derives from the BindableBase class. Therefore, each view model class uses the SetProperty method in the BindableBase class to provide property change notification.

1 public class PhotoPageViewModel : BindableBase, INavigationAware
2 {
3 ...
4 private string _image;
5 public string Image
6 {
7 get { return this._image; }
8 set { SetProperty(ref this._image, value); }
9 }
10 ...
11 }

The call to the SetProperty method simply updates the value of the property if it has changed, and provides property change notification to the view.

For more information about data binding in Xamarin.Forms, see Data Binding Basics.

Triggering actions in the view model from the view

Apps typically invoke an action in response to a user action, such as a button click, that can be implemented by creating an event handler in the view’s code-behind file. However, in the MVVM pattern, the responsibility for implementing the action lies with the view model, and you should try to avoid placing code in the view’s code-behind file.

Commands provide a convenient approach to represent actions that can be bound to controls in the view. View models typically expose command properties, for binding from the view, that are object instances that implement the ICommand interface. XAML inherently supports commands and many controls provide a Command property that can be data bound to an ICommand object in the view model. For more information about Xamarin.Forms support for ICommand, see From Data Bindings to MVVM.

Prism provides the DelegateCommand class to implement commands in view models.

1 public DelegateCommand NavigateCommand { get; private set; }
2
3 ...
4
5 public PhotoPageViewModel(INavigationService navigationService)
6 {
7 ...
8 this.NavigateCommand = new DelegateCommand(GoBack);
9 }
10
11 private void GoBack()
12 {
13 this.NavigationService.GoBack();
14 }

The NavigateCommand is exposed to the view through a read-only property that’s initialized in the view model constructor.

1 <Button Command="{Binding NavigateCommand}"
2 Text="Go back" />

When the command is invoked from the view, it simply forwards the call to the GoBack method in the view model class via the delegate specified in the view model constructor.

Navigating between pages

The XamarinPhotoViewer sample app triggers navigation requests from user interaction in the views. These requests are to navigate between the MainPage and PhotoPage views.

Prism’s INavigationAware interface allows an implementing view model class to participate in a navigation operation. This interface defines the OnNavigatedFrom and OnNavigatedTo methods that are called during a navigation operation. The OnNavigatedFrom method allows the page being navigated away from to perform any cleanup before it is disposed of. In the view model class for the page being navigated to, its OnNavigatedTo method is called after navigation is complete. The OnNavigatedTo method allows the newly displayed page to initialize itself by using any navigation parameters passed to it. For example, the OnNavigatedTo method in the PhotoPageViewModel class accepts a Photo parameter that is used to display data on the view.

Prism provides the PageNavigationService class that allows view models to perform navigation operations without taking a dependency on UI types. When view model classes are instantiated, Unity will inject the dependencies that are required including the PageNavigationService instance. View models can then invoke the Navigate method on the PageNavigationService instance to cause the app to navigate to a particular view in the app, or the GoBack method to return to the previous view.

1 public void Navigate()
2 {
3 var parameters = new NavigationParameters();
4 parameters.Add("photo", this.Photo);
5 this.NavigationService.Navigate("PhotoPage", parameters);
6 }

The Navigate method accepts a string parameter that represents the page to be navigated to, and a navigation parameter that represents the data to pass to the page being navigated to. Any data being passed to the page being navigated to will be received by the OnNavigatedTo method of the view model class for the page type.

Placing the navigation logic in view model classes enables navigation logic to be exercised through automated tests. In addition, view models can then implement logic to control navigation to ensure that any required business rules are enforced.

Summary

Prism for Xamarin.Forms provides loosely-coupled components that help you to produce Xamarin.Forms apps that are flexible, maintainable, and testable. In particular, Prism’s support for MVVM enables you to easily provide a clean separation of concerns between the appearance and layout of the UI from the responsibility for the business logic. This helps to ensure that an app will be easier to test, maintain, and evolve.

The sample app can be downloaded here.

1 comment:

  1. Great post! Just so you know, Prism for Xamarin.Forms has been updated with some nice improvements. Check them out and let me now what you think:

    http://brianlagunas.com/whats-new-in-prism-for-xamarin-forms-5-7-0-pre1-release/

    ReplyDelete