Thursday, 22 June 2017

Xamarin with MVVM Cross Framework

Introduction:
MvvmCross is a cross-platform MVVM framework that enables developers to create powerful cross platform apps. It supports Xamarin.iOS, Xamarin.Android, Xamarin.Mac, Xamarin.Forms, Universal Windows Platform (UWP) and Windows Presentation Framework (WPF).

·      The high level features that MvvmCross provides you with are:
·      MVVM architecture pattern
·      Navigation system
·      Data Binding
·      Platform specifics support
·      Inversion of control and Dependency Injection
·      Lots of plugins for common functionalities
·      Unit test helpers
·      Complete flexibility
           
Your first project with Xamarin and MvvmCross:
Here is a video that will help you creating a project from scratch with Xamarin and MvvmCross:


It gives all the detailed steps for a solution that includes a Core PCL and app projects for iOS, Android and Windows Phone.

Here is the article that contains the text version:


MvvmCross Overview:
Deployed MvvmCross applications consist of two parts:
the “core” - containing all the ViewModels, Services, Models and ‘business’ code
the “ui” - containing the Views and platform specific code for interacting with the “core”
For a multi-platform application, it’s typical for there to be:
·      a single “core” project, written as a PCL (Portable Class Library)
·      a “ui” project per platform written as a native project for the current target platform.
optionally some “plugins” - each one containing a PCL part and native parts - each one providing reusable abstractions of native functionality such as camera, geolocation, accelerometer, files, etc.

This is the way that MvvmCross encourages people to write their applications, and this guide will. However, other approaches are possible - e.g. a single project can include both “core” and “ui”, or multiple “core” projects can be written using copy-and-paste or using a technique such as file-linking.

Some key MvvmCross objects
There are a few key objects within an MvvmCross application:
In the “core”, there are:
An App - responsible for starting your ViewModels and your business logic
A Start object - responsible for deciding the first ViewModel or ViewModels which should be presented
·      one or more ViewModels - each one responsible for a piece of user interaction
·      your services, models, etc
In each “ui”, there are:
The native Application object - responsible for native lifecycle events - on each platform this object is a platform-specific class
·      An MvvmCross Setup class - responsible for ‘bootstraping’ MvvmCross, your ‘core’ and your ‘ui’.
·      One or more Views - each one responsible for presenting one of your ViewModels
·      A Presenter - responsible for deciding how Views are shown
·      Custom UI code - for controls, gestures, events, etc
·       
How an MvvmCross application starts:

·      When an MvvmCross app starts on a native project, then:
·      The native Application will ‘be created’ first
·      Within the construction of the native Application, a Setup will be created
·      The Setup will perform very basic tasks - e.g. initialisation of the IoC system (see https://github.com/slodge/MvvmCross/wiki/Service-Location-and-Inversion-of-Control)
·      Then the Setup will call into the core project, construct an App and will call Initialize on it.
  • during the Initialize your App will typically:
    • register your app-specific services with the IoC system
    • create and register a Start object
·      The Setup will then configure the UI side of the project - especially things like lookup tables for views
·      Finally the Setup will start the MvvmCross binding engine (if needed)
·      With Setup complete, your native Application can then actually start.
·      To do this, it requests a reference to the Start object and calls Start() on it
·      After this, the app will start presenting ViewModels using databound Views

The MvvmCross Core
An MvvmCross Core project provides:
·      An application object - typically in App.cs
·      One or more ViewModels - normally in a folder called ViewModels
·      Your code: services, models, repositories, engines, units of work, etc - whatever your app needs to work

App.cs:
In each MvvmCross application there should be one and only one App.
This App is not to be confused with the ApplicationDelegate in iOS, or with the Application objects in Android or Windows. Those native objects are there to provide the lifecycle of the native platform-specific code.
Instead, this App in MvvmCross is there to assist with the lifecycle of your ViewModels and your services, models, etc
The key methods within an App are:
·      Initialize - called on start up
·      FindViewModelLocator(MvxViewModelRequest request) - used to find the object which provides ViewModels during navigation
The specific jobs that your App should do during its Initialize are:
·      To construct and/or IoC-register any objects specific to your applications - services, models, etc
·      To register an IMvxAppStart object

A default App supplied via nuget, looks like:

using MvvmCross.Platform.Ioc;

namespace MyName.Core
{
    public class App : MvvmCross.Core.ViewModels.MvxApplication
    {
        public override void Initialize()
        {
            CreatableTypes()
            .EndingWith("Service")
            .AsInterfaces()
            .RegisterAsLazySingleton();

            RegisterAppStart<ViewModels.FirstViewModel>();
        }
    }
}

This App:
·      within Initialize
·      looks within the current Assembly (the “core” Assembly) and uses Reflection to register all classes ending in Service as lazily-constructed singletons
·      calls RegisterAppStart<TViewModel> to create and register a very simple IMvxAppStart implementation - an implementation which always shows a single FirstViewModel when Start() is called
·      uses the default ViewModelLocator - this default uses naming conventions to locate and construct ViewModels and creates a new ViewModel for each and every request from a View
If you wanted to use a custom IMvxAppStart object, see https://github.com/slodge/MvvmCross/wiki/Customising-using-App-and-Setup.

ViewModels
In each MvvmCross ‘core’ application your ViewModels provide containers for the state and the behaviour for your User Interface.
Typically they do this using:
·      C# Properties
·      The INotifyPropertyChanged and INotifyCollectionChanged interfaces to send notifications when properties change
·      Special ICommand properties which can allow View events (e.g. button taps) to call actions within the ViewModel

For MvvmCross, ViewModels normally inherit from MvxViewModel

A typical ViewModel might look like:
public class FirstViewModel
    : MvxViewModel
{
    private string _name;
    public string Name
    {
        get {
            return _name;
        }
        set {
            _name = value;
            RaisePropertyChanged(() => Name);
        }
    }

    private MvxCommand _resetCommand;
    public ICommand ResetCommand
    {
        get
        {
            _resetCommand = _resetCommand ?? new MvxCommand(() => Reset());
            return _resetCommand;
        }
    }

    private void Reset()
    {
        Name = string.Empty;
    }
}

This FirstViewModel has:
·      A single Name property which raises a PropertyChanged notification when it changes
·      A single ResetCommand command which will call the Reset() method whenever the command is executed.
·       
Beyond this simple example, ViewModels can also:

 (see https://github.com/slodge/MvvmCross/wiki/MvvmCross-Tutorials#working-with-collections)
Be constructed from IoC
(https://github.com/slodge/MvvmCross/wiki/Service-Location-and-Inversion-of-Control)
use ‘techniques’ like:


Fody to remove some of the boilerplate code
(http://slodge.blogspot.co.uk/2013/07/awesome-clean-viewmodels-via-fody.html)

The MvvmCross UI
An MvvmCross ‘ui’ project provides:
·      The native platform-specific application code - e.g Main.cs and AppDelegate.cs on Xamarin.iOS
·      A Setup.cs class
·      One or more Views - each one responsible for presenting one of your ViewModels
·      A Presenter - responsible for deciding how Views are shown
·      Custom UI code - for controls, gestures, events, etc
·       
Platform specific application code
iOS

On iOS, we need to replace the normal AppDelegate.cs class with an MvxApplicationDelegate

An initial replacement looks like:

using Foundation; using UIKit;
using MvvmCross.Platform;
 using MvvmCross.iOS.Platform;
using MvvmCross.Core.ViewModels; 
namespace MyName.iOS {   
 [Register ("AppDelegate")]    
public partial class AppDelegate : MvxApplicationDelegate   
 {        
UIWindow _window;        
 public override bool FinishedLaunching (UIApplication app, NSDictionary options)         {            
_window = new UIWindow (UIScreen.MainScreen.Bounds);             
var setup = new Setup(this, _window);            
setup.Initialize();             
var startup = Mvx.Resolve<IMvxAppStart>();            
startup.Start();             
_window.MakeKeyAndVisible ();            
 return true;          }    } }

Android
On Android, we don’t normally have any Application to override. Instead of this, MvvmCross by default provides a SplashScreen - this typically looks like:

using Android.App;
using Android.Content.PM;
using MvvmCross.Droid.Views; 
namespace MyName.Droid
{     [Activity(          Label = "CustomBinding.Droid"  , MainLauncher = true  , Icon = "@drawable/icon"          , Theme = "@style/Theme.Splash"          , NoHistory = true          , ScreenOrientation = ScreenOrientation.Portrait)]    
public class SplashScreen : MvxSplashScreenActivity    
{         public SplashScreen()         : base(Resource.Layout.SplashScreen)   
     {         }     }  }

Importantly, please note that this class is marked with MainLauncher = true to ensure that this is the first thing created when the native platform starts.

Wpf
On Wpf, a new project will contain a native App.xaml.cs. After adding the MvvmCross libraries via Nuget a new file is added called ‘App.Xam.Mvx.cs’. This file contains -
using System;
using System.Windows;
using MvvmCross.Core;
using MvvmCross.Core.ViewModels;
using MvvmCross.Wpf.Views; 
namespace MyName.Wpf
{    
public partial class App : Application    
{         private bool _setupComplete;         
private void DoSetup()        
{            
LoadMvxAssemblyResources();             
var presenter = new MvxSimpleWpfViewPresenter(MainWindow);             
var setup = new Setup(Dispatcher, presenter);            
setup.Initialize();             
var start = Mvx.Resolve<IMvxAppStart>();            
start.Start();             
_setupComplete = true;       
 }         
protected override void OnActivated(EventArgs e)        
{            
if (!_setupComplete)                
DoSetup();              
base.OnActivated(e);      
 
}         
private void LoadMvxAssemblyResources()       
 {           
 for (var i = 0;; i++)    
 {                
string key = "MvxAssemblyImport" + i;            
var data = TryFindResource(key);                
if (data == null)                    
return;           
      }          }      } }

A default FirstView should also exist.

Uwp
On Uwp, a new project will again contain a native App.xaml.cs
To adapt this for MvvmCross, we simply find the method OnLaunched and replace the if (rootFrame.Content == null) block with:

var setup = new Setup(rootFrame); setup.Initialize();
 var start = Mvx.Resolve<IMvxAppStart>();
 start.Start();

Setup.cs:

The Setup class is the bootstrapper for the MvvmCross system.
This bootstrapper goes through a lot of steps, and almost all of these are virtual allowing you to customise MvvmCross.



Some key ones you should be aware of are:
·      CreateApplication - your Setup must override this one in order to provide a new instance of your App object from your core project
·      InitializeFirstChance - a “first blood” placeholder for any steps you want to take before any of the later steps happen
·      CreateDebugTrace - a chance to customise where application trace is placed - see http://stackoverflow.com/a/17234083/373321 for an example
·      InitializeLastChance - a “last ditch” placeholder for any steps you want to take after all of earlier steps have happened. Note that the Android and iOS base Setup classes use ‘last chance’ for initializing the UI data-binding system, so it’s important to always call base.InitializeLastChance() in your override.
·       
Beyond this, a larger list of Setup customisation options is discussed in https://github.com/slodge/MvvmCross/wiki/Customising-using-App-and-Setup

Minimal Setup – Android
using Android.Content;
using MvvmCross.Droid.Platform;
using MvvmCross.Core.ViewModels; 
namespace MyName.Droid
{    
public class Setup : MvxAndroidSetup    
{       
 public Setup(Context applicationContext) : base(applicationContext)        
{         }        
 protected override IMvxApplication CreateApp()         {          
  return new Core.App();        
 }     } }

Minimal Setup – iOS
using UIKit;
using MvvmCross.iOS.Platform; 
namespace MyName.iOS {   
 public class Setup : MvxIosSetup     {        
public Setup(MvxApplicationDelegate applicationDelegate, UIWindow window)         : base(applicationDelegate, window)         {
        }       
  protected override IMvxApplication CreateApp ()         {           
 return new Core.App();         }     } }

Minimal Setup – Wpf

using System.Windows.Threading;
using MvvmCross.Platform;
using MvvmCross.Core.ViewModels;
using MvvmCross.Wpf.Platform;
using MvvmCross.Wpf.Views; 
namespace MyName.Wpf {    
public class Setup : MvxWpfSetup     {        
public Setup(Dispatcher dispatcher, IMvxWpfViewPresenter presenter)         : base(dispatcher, presenter)        
{         }         
protected override IMvxApplication CreateApp()         {            
return new Core.App();         }       
  protected override IMvxTrace CreateDebugTrace()         {            
return new DebugTrace();         }     } }

Setup – Uwp
using MvvmCross.ViewModels;
using MvvmCross.Uwp.Platform;
using Windows.UI.Xaml.Controls; 
namespace MyName.Store {    
public class Setup : MvxWindowsSetup   
 {         public Setup(Frame rootFrame) : base(rootFrame)       
 {         }        
 protected override IMvxApplication CreateApp()         {           
 return new Core.App();         }     } }

Views
Each UI Platform needs a set of Views
Each View is normally databound to a single ViewModel for its entire lifetime.
On each platform, Views in the Mvvm sense are typically implemented using data-bound versions of:
·      On Windows platforms a UserControl - for Uwp is very often specialised into a Page
·      On Android, an Activity or Fragment
·      On iOS, a UIViewController

Within this introduction we won’t go further into how these Views are actually written - instead see the introductions to data-binding on each platform within the TipCalc tutorial.
One important thing to note, is that by default Views are associated with ViewModels using a naming convention in MvvmCross. This can be overridden if required (see the https://github.com/slodge/MvvmCross/wiki/Customising-using-App-and-Setup#overriding-view-viewmodel-associations) - but by default the MvvmCross system links a View called FooView to a ViewModel called FooViewModel

A Presenter
Each UI Platform provides a Presenter which implements IMvxViewPresenter.
In default applications, the Presenter used normally fills the entire screen with a Page and allows back button navigation to previous pages.
When more advanced screen layouts are needed - e.g. flyouts, tabs, pivots, split-screens, etc - then these can be supplied by using a custom presenter. For more on this, see http://slodge.blogspot.co.uk/2013/06/presenter-roundup.html

REFERENCE LINKS:







No comments:

Post a Comment