Using MvvmCross with Xamarin.Forms — Part 1

Martijn van Dijk
16 min readJul 13, 2017

--

A comprehensive story and complete guide to implementing MvvmCross with Xamarin.Forms!

Introduction to Mvvm and MvvmCross

What is MVVM?

When you start to write apps, you probably sit down and think about how you will structure your code, which frameworks you are going to use, and how you can manage the whole project. There are some variables to take into account like, will you work together with a team, is a designer involved, or do team members have different roles? What you don’t want is to have to wait for another person to finish something before you can continue your work.

The solution for this is to separate concerns! Make sure someone can work on the front-end code while another person is working on on the code-behind. There are a few concerns we can identify when building apps:

  • Business logic or back-end logic (Model)
  • Graphical user interface (View)
  • Controller to manage and present Models to the View (ViewModel)

In contrast to other patterns like MVC(Model, View, Controller) or MVP(Model, View, Presenter), Mvvm abstracts a view’s state and behavior, meaning the ViewModel is responsible for exposing and converting the data from the Model in such a way that objects are easily managed and presented to the View.

So MvvmCross implements MVVM?

MvvmCross is an opinionated take on MVVM. It follows the same pattern, and adds a couple of nifty extras. It is a framework specifically developed for Xamarin and the mobile ecosystem. With this in mind there are a couple of very handy features that you can leverage to build apps even faster and of higher quality.

What does Xamarin.Forms do?

Xamarin.Forms let you build native UIs for iOS, Android and Windows from a single, shared codebase. This means that apps can share a large portion of their graphical user interface and still retain the native look and feel of the target platform. This is great for apps that require little platform-specific functionality, where code sharing is more important than custom UI, or when developers are comfortable with XAML (coming from a WPF or WindowsPhone background). Note that it isn't required to use XAML for the UI, you can also make the layouts in plain C#.

When we take a look at the basic architecture patterns of Forms, we see some of the same concepts of MvvmCross. Forms implements Mvvm as well, and offers built-in support for Dependency injection, multiple Navigation patterns, Data-binding and messaging between loosely coupled components.

Why would you use another MVVM framework when you already use Forms?

Xamarin.Forms is, first and foremost, an abstracting of the UI. While it offers some basic functionality to get you going, other Mvvm frameworks help you to structure and optimize your code in an even better way. We will take a deep dive into those advantages later in this blog!

The most important thing to think about when choosing whether to use Xamarin.Forms, MvvmCross, or any other framework, is what the goals of your app are.

  • Is it going to be a big project?
  • Does it need to be maintained over a longer period of time?
  • Do you need access to native UI, native (hardware) functionality or platform-specific APIs?

When you have a clear vision of these objectives, it might even be better building a Native Xamarin app without using Xamarin.Forms at all!

In any case, the main benefit is that you start off with a separate “MVVM” project for your shared code, and you can (re)use it if you ever decide to switch to Native Xamarin, or another UI framework.

Projects using Xamarin.Forms that aim to optimize development, have great maintainability and be future-proof are a good fit for MvvmCross!

What are the advantages of MvvmCross?

Lets say we have a project perfectly fitted to do Xamarin.Forms, you want to optimize your development process, have great maintainability of code, and be ready for the future. A good fit for this would be MvvmCross! The main benefits you instantly get when using MvvmCross are:

  • Fully customizable code where you as developer are in control
  • Presenters that enable you to change native platform navigation
  • ViewModel to ViewModel navigation, with support for passing and returning objects
  • Built-in Inversion of Control, Dependency Injection and Bindings
  • Helpers and classes to build amazing apps
  • Plugins to drop in and just work!
  • A large and engaged community to help you

Which platforms are supported?

MvvmCross itself offers support for a wide range of platforms, and as for Xamarin.Forms support we have:

  • Portable class library
  • Android
  • iOS
  • Universal Windows Project (UWP)

In the future we will add support for more platforms, as Forms platforms get updated to stable. This will include MacOS, Tizen, WPF, Linux (GTK#) and more!

Implementing MvvmCross with Xamarin.Forms

Let's take a look at setting up a project

MvvmCross has some very helpful Nuget packages to get you started. In this case we will use the StarterPack Nuget to install the basic files and the MvvmCross.Forms Nuget to get us connected to Forms. Another great way is to use a Visual Studio extension like XabluCross for MvvmCross. In this guide I’m going to use Visual Studio for Mac to develop the sample App. You should be able to do the same using Visual Studio 2017, but things might just look slightly different.

Note: this tutorial is using MvvmCross 5.1.0. Using other versions may not work!

  • Open up Visual Studio and start creating a New Solution in the File menu in the Menu.
  • In the Multi-platform section select Blank Forms App.
  • Enter the name for your new project, for this sample we will call it: MvxForms
  • As Target platforms we leave the default selected ones, Android and iOS.
  • For shared code we pick Portable Class Library. More information on that is available in the MvvmCross documentation.
  • In this we will use XAML for our layouts so leave that on default.
  • In the next page we leave the Project name and solution name to MvxForms. I would advice to create a .gitignore file if you are using Git. Press Create to finish.
  • In MvvmCross it is common to name the shared code project ".Core". Change the name from MvxForms to MvxForms.Core.
  • Open the Core, Android and iOS projects, double click Packages and add the MvvmCross.StarterPack nuget and the MvvmCross.Forms nuget.
  • Remove the ToDo-MvvmCross and Views folders in the Android and iOS projects.

It might be a good idea to manually remove the MvvmCross.Starterpack from the packages.config file, otherwise your files will be overwritten the next time you update your nugets.

  • Change the App.cs name from:
public class App : MvvmCross.Core.ViewModels.MvxApplication

To:

public class CoreApp : MvvmCross.Core.ViewModels.MvxApplication

We do this because otherwise the Forms.App class conflicts with the MvvmCross.App class.

  • Edit the corresponding App.xaml file from:
<FormsApplication xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MvxForms.Core.App">
...
</FormsApplication>

To:

<d:MvxFormsApplication xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MvxForms.Core.App"
xmlns:d="clr-namespace:MvvmCross.Forms.Core;assembly=MvvmCross.Forms">
...
</d:MvxFormsApplication>
  • Do the same for App.xaml.cs
public partial class App : FormsApplication

To:

public partial class App : MvxFormsApplication
  • Remove the line 'MainPage = MvxFormsPage();' from App.xaml.cs.
  • In the Setup.cs class of Android and iOS, change:
protected override IMvxApplication CreateApp()
{
return new Core.App();
}

To:

protected override IMvxApplication CreateApp()
{
return new Core.CoreApp();
}
protected override MvvmCross.Forms.Core.MvxFormsApplication CreateFormsApplication()
{
return new App();
}
  • Also change the base class of the Android setup from MvxAndroidSetup to MvxFormsAndroidSetup. Do the same on iOS from MvxIosSetup to MvxFormsIosSetup.
  • Note, if you get the error: No resource found that matches the given name (at ‘icon’ with value ‘@mipmap/icon’). Go to SplashScreen.cs and change Icon = “@mipmap/icon” to Icon = “@drawable/icon”.
  • On Android in your Activity inherit from MvxFormsAppcompatActivity and replace theOnCreate method with:
protected override void OnCreate(Bundle bundle)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;

base.OnCreate(bundle);
}

If you are using a SplashScreen on Android you need to remove the mainlauncher = true from the Activity attribute. If your ViewModel is called MainViewModel and your Forms page is too, you might get a name conflict because MvvmCross will have 2 view to viewmodel lookups. You can prevent this by naming your Activity differently like "FormsActivity.cs".

  • On iOS replace your AppDelegate code with:
public partial class AppDelegate : MvxFormsApplicationDelegate
{
public override UIWindow Window { get; set; }

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();

LoadApplication(setup.FormsApplication);

Window.MakeKeyAndVisible();

return true;
}
}
  • The last step is to make your Xamarin.Forms page extend the MvxContentPage. You can either use a generic or name based convention to make MvvmCross recognize it. In MvxFormsPage.xaml.cs change:
public partial class MvxFormsPage : ContentPage

To:

public partial class MvxFormsPage : MvxContentPage<MainViewModel>
  • You need to change the XAML page attached to it as well:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MvxForms.Core">
...

To:

<d:MvxContentPage x:TypeArguments="viewModels:MainViewModel"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MvxForms.Core"
x:Class="MvxForms.Core.MvxFormsPage"
xmlns:viewModels="clr-namespace:MvxForms.Core.ViewModels;assembly=MvxForms.Core"
xmlns:d="clr-namespace:MvvmCross.Forms.Core;assembly=MvvmCross.Forms">
...
  • Note, if you are using a SplashScreen on Android you need to the following snippet to it:
protected override void TriggerFirstNavigate()
{
StartActivity(typeof(MainActivity));
base.TriggerFirstNavigate();
}

Otherwise your Forms Activity wouldn't run and you'll stuck on the splashScreen.

Fire up the App and enjoy!

The result of this tutorial is available at: https://github.com/martijn00/MvxForms

How does MvvmCross know which ViewModel is connected to a View?

By default this is done using naming conventions. If your View is called LoginView then the ViewModel it will look for should be called LoginViewModel. Another way is using Generics to indicate the ViewModel to use. I would recommend this since it is type safe.

public partial class LoginPage : MvxContentPage<LoginViewModel> 
{
public MainPage()
{
InitializeComponent();
}
}

The XAML layout for this page would then look like:

<core:MvxContentPage
x:TypeArguments="viewModels:LoginViewModel"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:core="clr-namespace:MvvmCross.Forms.Core;assembly=MvvmCross.Forms"
xmlns:viewModels="clr-namespace:MvxForms.ViewModels;assembly=MvxForms"
x:Class="MvxBindingsExample.Pages.LoginPage">
...
</core:MvxContentPage>

Using MvvmCross style bindings in Forms layouts

To use the native MvvmCross bindings within a Xamarin.Forms page simply add a reference to MvvmCross.Forms.Bindings like this: xmlns:mvx="clr-namespace:MvvmCross.Forms.Bindings;assembly=MvvmCross.Forms" in the XAML page.

After you've added the reference you will be able to bind to any supported control using mvx:Bi.nd="". This could for example look like:

<core:MvxContentPage
x:TypeArguments="viewModels:LoginViewModel"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:core="clr-
namespace:MvvmCross.Forms.Core;assembly=MvvmCross.Forms"
xmlns:viewModels="clr-
namespace:MvxForms.ViewModels;assembly=MvxForms"
xmlns:mvx="clr-
namespace:MvvmCross.Forms.Bindings;assembly=MvvmCross.Forms"
x:Class="MvxBindingsExample.Pages.LoginPage"
<StackLayout Margin="5">
<Label Text="Default (OneWay) Binding" />
<Label mvx:Bi.nd="Text BindableText" />
<Label Text="OneWayToSource Binding" />
<Entry mvx:Bi.nd="Text BindableText, Mode=OneWayToSource" />
<Label Text="TwoWay Binding" />
<Entry mvx:Bi.nd="Text BindableText, Mode=TwoWay" />
<Label Text="OneTime Binding" />
<Label mvx:Bi.nd="Text BindableText, Mode=OneTime" />
<Label Text="Lang Binding" />
<Label mvx:La.ng="Text ThisIsLocalized" />
</StackLayout>
</core:MvxContentPage>

So this means that I have to use the 'mvx' binding extensions, not Forms built in binding?

Using the MvvmCross bindings doesn't exclude using Xamarin.Forms bindings, but there are some clear advantages when you use 'mvx' bindings.

  • Multiple binding annotations
  • All the built-in value converters that MvvmCross offers like: Visibility, Color, Localization
  • Access to the value-combiners: If, Format,And and Or, and For
  • Native bindings in the same annotation when mixing Forms with native views

More information about this is available in the documentation.

I want to navigate between ViewModels, pass objects and return results to them

MvvmCross uses ViewModel First Navigation. This means that we navigate from ViewModel to ViewModel and not from View to View. It ships with its own navigation system called IMvxNavigationService to navigate between these ViewModels. This enables you to pass an object to a new ViewModel and await a result when you close a ViewModel that is on top of another one. This would look like:

public class MyViewModel : MvxViewModel
{
private readonly IMvxNavigationService _navigationService;
public MyViewModel(IMvxNavigationService navigation)
{
_navigationService = navigationService;
}

public async Task SomeMethod()
{
var result = await _navigationService.Navigate<NextViewModel, MyObject, MyReturnObject>(new MyObject());
//Do something with the result MyReturnObject that you get back
}
}

public class NextViewModel : MvxViewModel<MyObject, MyReturnObject>
{
public async Task Initialize(MyObject parameter)
{
//Do something with parameter
}

public async Task SomeMethod()
{
await Close(new MyReturnObject());
}
}

For extensive documentation about this look at the Navigation documentation.

How does Deeplinking with the NavigationService work?

The NavigationService supports multiple URIs per ViewModel as well as “NavigationFacades” that return the right ViewModel + parameters depending on the URI.

To use URI navigation supply your routings as assembly attributes. I would recommend putting them in the same file as the referenced ViewModel.

[assembly: MvxNavigation(typeof(ViewModelA), @"mvx://test/\?id=(?<id>[A-Z0-9]{32})$")]
namespace *.ViewModels
{
public class ViewModelA : MvxViewModel
{
public void Initialize(string id) // you can use captured groups defined in the regex as parameters here
{
}
}
}

Depending on the status of the app you can pass a URI as the Notification Parameter, so when the app starts you can deep link directly to the view you want. From a ViewModel perspective this would look like:

public class MainViewModel : MvxViewModel
{
private readonly IMvxNavigationService _navigationService;

public MainViewModel(IMvxNavigationService navigationService)
{
_navigationService = navigationService;
}

private IMvxAsyncCommand _showACommand;
public IMvxAsyncCommand ShowACommand
{
get
{
return _showACommand ?? (_showACommand = new MvxAsyncCommand(async () =>
{
await _navigationService.Navigate("mvx://test/?id=" + Guid.NewGuid().ToString("N"));
}));
}
}
}

How does this compare to the built-in navigation of Forms?

MvvmCross uses Presenters to navigate to views on the native platform. It is a singleton that acts as a glue between your Views and your ViewModels. As the name implies, a view presenter takes a ViewModel presentation request and decides how it will be presented on the UI. By using View Presenters, MvvmCross provides a clear separation between the ViewModel and the View layer

This is also the key to what enables us to show any Native, Forms view or mixing it while navigating. As we saw earlier the IMvxNavigationService lets you pass objects and return objects to any awaiting ViewModel. This is functionality that Forms doesn't offer.

It is really easy to change the behaviour of the NavigationService or the View Presenter. Just override either of them to implement your own version. Using this to your advantage is a very powerful tool to build next generation apps!

Show me how to use Dependency Injection(DI) and Inversion of Control(IoC) to access native APIs!

There are lots of articles and introductions available on this. Some good starting places are Martin Fowler’s introduction and Joel Abrahamsson’s IoC introduction.

Specifically within MvvmCross, we provide a single static class Mvx which acts as a single place for both registering and resolving interfaces and their implementations.

As you could see in the last samples we used Dependency Injection to inject the IMvxNavigationService into the ViewModel. In fact this is under the hood registered to the MvvmCross IoC container!

Sometimes you need to use some platform-specific functionality in your ViewModels. For example, you might want to get the current screen dimensions in your ViewModel , but there’s no existing PCL .Net call to do this. What we can do is declare an interface in the Core project, but then provide and register an implementation in each of your UI platform projects.

Create an interface in your PCL-Core project
In your project, you can declare an interface and you can use that interface in any of your classes there:

public interface IScreenSize
{
double Height { get; }
double Width { get; }
}
public class MyViewModel : MvxViewModel
{
private readonly IScreenSize _screenSize;
public MyViewModel(IScreenSize screenSize)
{
_screenSize = screenSize;
}
public double Ratio => (_screenSize.Width / _screenSize.Height);
}

In each UI project, you can then declare the platform-specific implementation for IScreenSize - a trivial example is:

public class AndroidScreenSize : IScreenSize
{
public double Height { get { return 800.0; } }
public double Width { get { return 480.0; } }
}

You can then register these implementations in each of the platform-specific Setup files — e.g. you could override MvxSetup.InitializeFirstChancewith

protected override void InitializeFirstChance()
{
Mvx.RegisterSingleton<IScreenSize>(new AndroidScreenSize());
base.InitializeFirstChance();
}

With this done, the MyViewModel will be provided with the correct platform specific implementation of IScreenSize on each platform.

What if I want to transform a value into something else?

This is trivial with the built-in MvxValueConverter. Value Converters in MvvmCross are used to provide mappings to/from logical values in the view models and presented values in the user interface.

For example, a value converter for converting DateTime in the ViewModel to ‘time ago’ string in the View might look like:

public class MyTimeAgoValueConverter : MvxValueConverter<DateTime, string>
{
protected override string Convert(DateTime value, Type targetType, object parameter, CultureInfo cultureInfo)
{
var timeAgo = DateTime.UtcNow - value;
if (timeAgo.TotalSeconds < 30)
{
return "just now";
}

if (timeAgo.TotalMinutes < 10)
{
return "a few minutes ago";
}

if (timeAgo.TotalMinutes < 60)
{
return "in the last hour";
}

if (timeAgo.TotalMinutes < 24*60)
{
return "in the last day";
}

return "previously";
}
}

Binding to for example a Label and converting the DateTime is as easy as:

<Label mvx:Bi.nd="Text MyTimeAgo(SomeDateTimeProperty)" />

How can I make use of the MvvmCross lifecycle events?

When a ViewModel is loaded a couple of events and methods will be triggered. You can use those to execute code that need to happen in different situations. It will give you more refined control of the ViewModel and the state of its lifecycle. There may be binding that you want to update or resources to clean up, and these lifecycle events can help with that.

public class MyViewModel : MvxViewModel
{
public override void ViewAppearing(){}
public override void ViewAppeared(){}
public override void ViewDisappearing(){}
public override void ViewDisappeared(){}
}

In any case the ViewModel will call the Task Initialize(){} method where you should do all your initial loading of data. Try to use async functions and caching to optimize the speed of your App.

As demonstrated before, if you pass an object to a ViewModel the Task Initialize(MyObject param){} will be called instead.

How does this compare to Xamarin.Forms lifecycle events?

The events in MvvmCross compliment the events in Forms. We even took inspiration from the proposal that was made for Xamarin.Forms.

Extending your App with MvvmCross

How can i use the MvvmCross plugins?

Using them is as simple as dropping in the Nuget package for it. Searching for MvvmCross Plugin on Nuget returns all the available packages. After that you can utilize them by either using dependency injection or resolving the interface. Let's have a look at one of the packages called Messenger.

The MvvmCross Messenger plugin provides an Event aggregation Messenger which is biased towards using Weak references for event subscription. You can define one or more Message classes for communication between components. These should inherit from MvxMessage:

public class LocationMessage : MvxMessage
{
public LocationMessage(object sender, double lat, double lng)
: base(sender)
{
Lng = lng;
Lat = lat;
}

public double Lat {
get;
private set;
}
public double Lng {
get;
private set;
}
}

Sending this message is simple as:

var message = new LocationMessage(
this,
location.Coordinates.Latitude,
location.Coordinates.Longitude
);

_messenger.Publish(message);

To receive the message in any ViewModel, subscribe to these type of messages. Each of these classes must call one of the Subscribe methods on the IMvxMessenger and must store the returned token. For example part of a ViewModel receiving LocationMessages might look like:

public class LocationViewModel : MvxViewModel
{
private readonly MvxSubscriptionToken _token;

public LocationViewModel(IMvxMessenger messenger)
{
_token = messenger.Subscribe<LocationMessage>(OnLocationMessage);
}

private void OnLocationMessage(LocationMessage locationMessage)
{
Lat = locationMessage.Lat;
Lng = locationMessage.Lng;
}
}

Other plugins that either follow bait & switch or implement .NET Standard are also easy to use. Some examples are:

Can we also open plain Native Fragments, ViewControllers or other views?

If you want to open a Native view without any MvvmCross or Forms, you need to implement your own presenter if you want to do it through MvvmCross. However if you just want to add a view that stands alone and handles it own business you could just add this to the platforms UI stack.

Does this also work with Forms Embedding that will come in Forms 3.0?

Right now we don't have built-in support for Forms Embedding, but this is planned when 3.0 hits stable. Most of the functionality that would enable this is already present in the current MvvmCross stable, so it would be a small step to add support later.

Tips & Tricks

What should I do to optimize performance?

Performance depends on a lot of different things. First of all, while Xamarin.Forms has quite good performance compared to other UI frameworks, it is still an abstraction that might slow down things. Taking into account that we are working with Forms there are a few tips and tricks I would like to share:

  • Follow best practices on mobile app development.
  • Use async await / Task the right way.
  • Use Xamarin UITest to see how Apps behave on different devices, so you can optimize your code. A good idea can be to use XabluUITest to implement a proper test structure.
  • Trick users by adding loading indicators and a splashscreen in a smart way. You can do this by using a WindowBackground on Android.
  • Use AOT compilation on Android.
  • Load functionality yourself, and don’t rely on reflection for this. You can easily do this by adding value converters, view assemblies, etc, to the Setup.cs of the platform you are working on.
protected override IEnumerable<Assembly> AndroidViewAssemblies => new List<Assembly>(base.AndroidViewAssemblies)
{
typeof(NavigationView).Assembly,
typeof(FloatingActionButton).Assembly,
typeof(Toolbar).Assembly,
typeof(DrawerLayout).Assembly,
typeof(ViewPager).Assembly,
typeof(MvxRecyclerView).Assembly
};

For more tips i would recommend to read 5 Ways to Boost Xamarin.Forms App Startup Time by David Ortinau!

Any other tips?

  • Get to know the native APIs behind Xamarin.Forms. When you know how Android, iOS or any other platform layouts work, it is easier to optimize your views for performance.
  • Use MFractor to squash bugs, generate code, navigate effortlessly and much more!

Should I migrate my Native Xamarin MvvmCross App to use Forms?

In my opinion it would probably be too big of a task to migrate all your code to use Xamarin.Forms. However, since it is possible to mix and match Native and Forms code, you can decide to build certain new features using Forms. Take into account the skillset of the people you work with, the requirements of the feature and the platforms specific API’s that are needed, and make an educated decision on what would work best in your situation.

More information

If you want to know more I recommend reading the MvvmCross documentation on MvvmCross & Forms. Other useful links are:

Part 2 coming soon!

Next time I'll show and talk about:

  • How to implement a Forms master-detail, TabbedPage and other views with MvvmCross.
  • How to integrate with backend services like Azure.
  • A complete overview of a recommended app architecture
  • Showing Modal views
  • Mixing Native Xamarin MvvmCross views with Xamarin.Forms Pages
  • Port an existing Forms app to use Mvx

Let me know if you have any other questions!

--

--

Martijn van Dijk

Microsoft & Xamarin MVP, maintainer of @MvvmCross, contributor to #Xamarin OSS. Co-Founder of @BaseflowIT