Rukesh has put together a great post, entitled Using C#, XAML + Uno Platform to Build One Codebase, Cross-Platform Apps, that provides a great walk through of getting started with the Uno Platform. It goes through grabbing the Uno extension for Visual Studio and then creating a new multi-platform application using just C# and XAML. … Continue reading “Using C#, XAML, Uno and MvvmCross to Jump Start Your Cross Platform Application”

Rukesh has put together a great post, entitled Using C#, XAML + Uno Platform to Build One Codebase, Cross-Platform Apps, that provides a great walk through of getting started with the Uno Platform. It goes through grabbing the Uno extension for Visual Studio and then creating a new multi-platform application using just C# and XAML. The end result is an app that runs on iOS, Android, Windows and of course WebAssembly. In this post I’m going to step through a similar process of creating an app but this time I’m going to reduce the amount of code I need to write by leveraging the MvvmCross framework.

Note: This post makes use of a pre-release version of MvvmCross that has been built specifically to work with Uno. I’ll add a local copy of the NuGet packages to this post but if you want to roll your own you can simply grab the Uno branch from my fork and build your own NuGet packages (right-click on the MvvmCross.Uno project and select Pack to generate the package).

Setting Up the Basic Application

We’re going to start by creating the basic structure of the application which will have: a head or target project for each platform we’re targeting (iOS, Android, UWP and WebAssembly), a shared project which will house the XAML files for our application, and a core library which will house our business logic (in this case just our ViewModels).

Creating the Uno Application

The head projects and the shared project solution structure we pretty much get for free by using the Cross-Platform App (Uno Platform) template.

We’ll give our project a name, MvxUnoStarter, and click Create to generate the solution structure.

Note: At this point I always make sure that each of the head projects, for iOS, Android, Windows and WebAssembly, are able to be run. Currently there are a few open issues that relate mainly to the template that’s part of the Uno extension. I cover most of these in my post https://nicksnettravels.builttoroam.com/uwp-splitview/. In short you need to remove the scale-200 from the image filenames in the shared project, you need to set the Target Android version and the iOS Deployment Target in order to get the projects to build.

Adding Core Library

Next, let’s add a library based on the Class Library (.NET Standard) project template.

We’ll call it MvxUnoStarter.Core

Make sure that you update all the head projects by adding a reference to the newly created core library. Right-click on each project and select Add, Reference from the context menu; then check the box next to the MvxUnoStarter.Core project.

Referencing Local MvvmCross.Uno NuGet Packages

Update 22/9/2019: Before you proceed with adding a reference to the MvvmCross.Uno packages, please make sure you update all Uno package references (Uno.Wasm.Bootstrap, Uno.UI and Uno.Core) to the latest prerelease version. Be careful that you don’t just update all package references as the Logging packages shouldn’t be updated as there’s a known incompatibility with Uno and the latest versions of those libraries.

Support for MvvmCross is still under development – if you want to get started with MvvmCross for Uno today you can either grab the updated source code from my fork (see note above) or you can grab the pre-built MvvmCross.Uno packages:

In order to make use of these packages you’ll need to setup a local package source (if you already have one setup on your machine you can just drop the packages into the folder for them to be available). Open the Options window, from the Tools menu, and locate the Package Sources node. Click the + button to add a new source; give the source a name (eg Local Mvx Packages) and make sure the Source is set to a folder on your computer where you have saved the packages.

Now that you have a local package source, right-click your solution in the Solution Explorer window and select Manage NuGet Packages for Solution. Change the Package source to the local package source and select the MvvmCross.Uno package (we’re not going to use the Plugins package in this post but if you want to try it out, feel free to add it to you projects too) and make sure you check all projects.

Avoiding Linker Issues

A common gotcha with working with WebAssembly is that the compilation process includes a fairly aggressive linker step. Make sure you update the LinkerConfig.xml file to include your core project (i.e. MvxUnoStarter.Core) and the MvvmCross library (i.e. MvvmCross.Uno and if you’ve added the plugins library, make sure MvvmCross.Uno.Plugins is included too).

Build and Run Your Projects

Before continuing I would highly recommend that you make sure all your head projects can be built and run. Go through each one and set them as the startup project and then run the solution. Note that for the Wasm project, don’t attempt to run with the debugger; instead use the Start Without Debugging (Ctrl+F5).

Application Views, ViewModels and Navigation

Now that we’ve done the basic house keeping and we have four head projects that all run, and have appropriate references to both the core project and MvvmCross, we’re all set to start creating some views and viewmodels.

Adding Views

We’ll start by creating two views for our application. Add a folder called Views to the Shared project and then add two new items to the folder, HomePage and SecondPage, both based on the Blank Page item template.

There are a couple of changes we need to make in order to take advantage of the navigation pattern used by MvvmCross. Both views need to inherit from MvxWindowsPage. We’ll update HomePage to include a TextBlock in the centre of the screen, bound to the property HelloWorld.

< views:MvxWindowsPage x:Class = "MvxUnoStarter.Shared.Views.HomePage" xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml" xmlns:views = "using:MvvmCross.Platforms.Uap.Views" Background = "{ThemeResource ApplicationPageBackgroundThemeBrush}" > < StackPanel HorizontalAlignment = "Center" VerticalAlignment = "Center" > < TextBlock Text = "{Binding HelloWorld}" /> </ StackPanel > </ views:MvxWindowsPage >

Next we’ll do the same for SecondPage.

< views:MvxWindowsPage x:Class = "MvxUnoStarter.Shared.Views.SecondPage" xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml" xmlns:views = "using:MvvmCross.Platforms.Uap.Views" Background = "{ThemeResource ApplicationPageBackgroundThemeBrush}" > < Grid HorizontalAlignment = "Center" VerticalAlignment = "Center" > < TextBlock Text = "{Binding HelloWorld}" /> </ Grid > </ views:MvxWindowsPage >

Adding ViewModels

The next thing to do is to create the corresponding viewmodels for the two pages: HomeViewModel and SecondViewModel. HomeViewModel will expose a property HelloWorld that simply returns a string to be displayed on the HomePage.

public class HomeViewModel : MvxNavigationViewModel { public string HelloWorld => "Hello World!!!" ; public HomeViewModel ( IMvxLogProvider logProvider, IMvxNavigationService navigationService ) : base ( logProvider, navigationService ) { } }

SecondViewModel is essentially the same but in this case the string is slightly different to indicate it’s on the second page.

public class SecondViewModel : MvxNavigationViewModel { public string HelloWorld => "Hello World - Page 2!!!" ; public SecondViewModel ( IMvxLogProvider logProvider, IMvxNavigationService navigationService ) : base ( logProvider, navigationService ) { } }

Registering the AppStart ViewModel

In order for MvvmCross to know which page to start on it’s necessary to register a viewmodel as the first viewmodel of the application. This is done by creating a class in the core project that inherits from MvxApplication and then calling RegisterAppStart in the Initialize method. In our case we’ll call this class App, with the following code:

public class App : MvxApplication { public override void Initialize ( ) { RegisterAppStart<HomeViewModel>(); } }

Updating App.xaml to Use MvvmCross

The last update to be made in order for the application to use MvvmCross, which is essentially the entry point for MvvmCross, is to update App.xaml (and the corresponding codebehind file App.xaml.cs) to inherit from MvxApplication. Note that due to a limitation with the XAML compiler’s ability to handle generics we actually need to create an intermediary class that defines the generic parameters, in this case called StartApp.

public abstract class StarterApp : MvxApplication < MvxWindowsSetup < Core . App >, Core . App > { }

Next we need to update App.xaml to inherit from StarterApp

< local:StarterApp x:Class = "MvxUnoStarter.App" xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local = "using:MvxUnoStarter" RequestedTheme = "Light" > </ local:StarterApp >

And then lastly, update the constructor of App.xaml.cs. As both MvvmCross support for Uno and the Uno support for Wasm are both prerelease there are a couple of changes, wrapped in the __WASM__ conditional block that are required in order for an MvvmCross based application to run using WebAssembly.

public App() { #if __WASM__ Windows.UI.Core.CoreDispatcher.HasThreadAccessOverride = true; MvxSetupSingleton.SupportsMultiThreadedStartup = false; #endif ConfigureFilters(LogExtensionPoint.AmbientLoggerFactory); InitializeComponent(); UnhandledException += App_UnhandledException; }

Navigation in MvvmCross

Currently, running the application will only show Hello World in the middle of the screen, as shown below. What we really want to do is to be able to navigate to the second view.

To do this, let’s add a button to the HomePage XAML.

< views:MvxWindowsPage x:Class = "MvxUnoStarter.Shared.Views.HomePage" xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml" xmlns:views = "using:MvvmCross.Platforms.Uap.Views" Background = "{ThemeResource ApplicationPageBackgroundThemeBrush}" > < StackPanel HorizontalAlignment = "Center" VerticalAlignment = "Center" > < TextBlock Text = "{Binding HelloWorld}" /> < Button Content = "Next" Command = "{Binding ShowSecondPageCommand}" /> </ StackPanel > </ views:MvxWindowsPage >

And then we need to add the ShowSecondPageCommand to the HomeViewModel that the button is data bound to.

public class HomeViewModel : MvxNavigationViewModel { public string HelloWorld => "Hello World!!!" ; public IMvxAsyncCommand ShowSecondPageCommand { get ; } public HomeViewModel ( IMvxLogProvider logProvider, IMvxNavigationService navigationService ) : base ( logProvider, navigationService ) { ShowSecondPageCommand = new MvxAsyncCommand( async () => await NavigationService.Navigate<SecondViewModel>()); } }

The ShowSecondPageCommand is instantiated with an MvxAsyncCommand object with the action to navigate to SecondViewModel. This is where the true power of MvvmCross comes in as the navigation to SecondPage is automatically handled behind the scenes.

MvvmCross for Uno

Whilst it feels like there’s a lot of hoops to jump through, once Uno and MvvmCross are setup, we get all the benefits of ViewModel to ViewModel navigation including dependency injection across iOS, Android, Windows and of course WebAssembly. Now you can rapidly build applications that target any platform.