Optimising Multi-Targeting with Visual Studio Solution Filters

Over time Visual Studio has progressively improved support for solutions that have a large number of projects. MvvmCross used to be over 200 projects to handle each of the target platforms it supports. With the introduction of multi-targeted projects the number of projects dropped significantly to around 50 projects. For example, the core MvvmCross project has 10 target framework monikers (TFM), instead of having a separate project for each framework. However, the decrease in load time (due to fewer projects) was offset by an increase in build time. Rebuilding the MvvmCross project triggers a build for each TFM, so the project is built 10 times. In this post, I’ll walk through a couple of techniques we use to reduce the build time when working with MvvmCross.

Whilst MvvmCross supports a wide range of target platforms, the reality is that when I’m making changes to MvvmCross I’ll be doing most of the work against one platform. For example being on Windows I might pick Android or Windows and use either the Playground or Playground.Forms sample apps to run up and test my changes. Since I’m working with only one platform at any given time, I don’t need every target framework to be built every time I make a change. Unfortunately there’s no way in Visual Studio to tell it not to build every target framework.

Conditional Target Frameworks

To get around the limitations of Visual Studio we’ve introduced some conditional logic into the project files that determine which TFMs are built. If we look at the top of the MvvmCross.csproj file we can see that there are a number of TFM lists.

<Project Sdk="MSBuild.Sdk.Extras">
  <PropertyGroup Condition=" '$(TargetsToBuild)' == 'All' ">
    <TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">netstandard2.0;net461;Xamarin.iOS10;Xamarin.Mac20;Xamarin.TVOS10;Xamarin.WatchOS10;MonoAndroid90;tizen40;netcoreapp2.1;uap10.0.16299</TargetFrameworks>
    <TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">netstandard2.0;net461;Xamarin.iOS10;Xamarin.Mac20;Xamarin.TVOS10;Xamarin.WatchOS10;MonoAndroid90;tizen40;netcoreapp2.1</TargetFrameworks>
  </PropertyGroup>

  <PropertyGroup Condition=" '$(TargetsToBuild)' != 'All' ">
    <TargetFrameworks Condition=" '$(TargetsToBuild)' == 'Android' ">netstandard2.0;MonoAndroid90;</TargetFrameworks>
    <TargetFrameworks Condition=" '$(TargetsToBuild)' == 'Uap' ">netstandard2.0;uap10.0.16299</TargetFrameworks>
    <TargetFrameworks Condition=" '$(TargetsToBuild)' == 'iOS' ">netstandard2.0;Xamarin.iOS10</TargetFrameworks>
  </PropertyGroup>

In the Directory.build.props file for MvvmCross, which we’ll come to in a minute, we’ve defined a property called TargetsToBuild. If this is set to ‘All’, we set the TargetFrameworks property to include all the TFMs (except when the OS isn’t Windows_NT where we leave out Uap). However, if the TargetsToBuild is something different we restrict the TargetFrameworks property to the appropriate TFM (eg MonoAndroid90 when the TargetsToBuild is ‘Android’). We also include netstandard2.0 to help ensure developers don’t accidentally include platform specific code in files that are used by all platforms.

I mentioned that we define the TargetsToBuild in the Directory.build.props file. Currently what you’ll find in the Directory.build.props file in the MvvmCross repository is something that looks like the following:

<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
  <TargetsToBuild>All</TargetsToBuild> 
  <!--<TargetsToBuild>Android</TargetsToBuild>-->
  <!--<TargetsToBuild>Uap</TargetsToBuild>--> 
  <!--<TargetsToBuild>iOS</TargetsToBuild>--> 
</PropertyGroup>

<PropertyGroup Condition=" '$(Configuration)' != 'Debug' ">
  <TargetsToBuild>All</TargetsToBuild>
</PropertyGroup>

The first PropertyGroup, which is used when the build Configuration is set to Debug, allows the developer to switch TargetsToBuild by simply commenting and uncommenting the different values. For example to switch to Android I simply comment out the line with ‘All’ in it and uncomment the ‘Android’ line. Note that you have to restart Visual Studio for this change to take effect.

Solution Filters (.slnf)

Using the TargetsToBuild property works really well and significantly cuts down on the build time when doing development with MvvmCross. However, whenever I step in to work on MvvmCross I find it frustrating that Visual Studio has to load all 50+ projects when I’m not going to use them all. I’ve noticed recently that a couple of OSS projects such as Platform.Uno and Allan Richie’s Shiny have started to include solution filter files (.slnf). Solution Filter files make it easy to open a solution with only a subset of projects loaded.

There’s plenty of documentation on how to create a solution filter file, so I’m not going to cover that here. However, for MvvmCross it makes sense to have different solution filters for work with each platform. In my PR I’ve created filters for All, Android, iOS and Uap, which match the different TargetsToBuild options (the reason for this will become evident shortly).

Launch Automation

I was looking around for a way to combine solution filtering with the conditional logic for TFMs. Unfortunately there’s currently no mechanism for linking them. What this means is that developers wanting to work for particular platform have to first set the TargetsToBuild in the Directory.build.props, and then open the solution using the appropriate filter file.

I figured that if I can’t link the solution filter with a specific list of TFMs, the least I could do was to automate the process of setting the TargetsToBuild and launching the solution. For this I created a series of .bat files (yeh, old school I know): LaunchVS.All.bat, LaunchVS.Android.bat, LaunchVS.iOS.bat and LaunchVS.Uap.bat. Each of these platform specific launch files invokes “LaunchVS.bat XXX” where XXX is the corresponding platform (eg “LaunchVS.bat Android”).

The LaunchVS batch file does two things:

  • Invokes a powershell command that does a find and replace to update a property, TargetsToBuildDeveloperOverride, in the Directory.build.props file.
  • Launches Visual Studio by starting the corresponding solution filter file. Hence the reason why the names of the filter files needed to match the platforms options for TargetsToBuild.
powershell -Command "(gc Directory.build.props) -replace '<TargetsToBuildDeveloperOverride>[a-zA-Z]*</TargetsToBuildDeveloperOverride>', '<TargetsToBuildDeveloperOverride>%~1</TargetsToBuildDeveloperOverride> ' | Out-File -encoding ASCII Directory.build.props"
start MvvmCross.%~1.slnf

At this point you might be wondering why we’re updating the TargetsToBuildDeveloperOverride property instead of TargetsToBuild. The reason for this is that if we simply did a find-and-replace using the TargetsToBuild property it would replace both the value used for the Debug configuration but also the other configurations. It’s important that we don’t modify the Release configuration by accidentally changing the TFMs, since this could then accidentally be committed to the repository. To avoid this problem I created the TargetsToBuildDeveloperOverride property which is only used to set the TargetsToBuild property for the Debug configuration.

<PropertyGroup>
  <TargetsToBuildDeveloperOverride>Uap</TargetsToBuildDeveloperOverride>          
</PropertyGroup>

<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
  <TargetsToBuild>$(TargetsToBuildDeveloperOverride)</TargetsToBuild> 
</PropertyGroup>

<PropertyGroup Condition=" '$(Configuration)' != 'Debug' ">
  <TargetsToBuild>All</TargetsToBuild>
</PropertyGroup>

With these changes, all a developer has to do when working with a particular platform is to double-click the appropriate .bat file. The Directory.build.props will be updated and the corresponding .slnf file will be launched. If the updated Directory.build.props does accidentally get committed to the MvvmCross repository it won’t affect the Release build configuration. The LaunchVS batch file is clever enough to switch from one platform to another without the developer having to undo changes to the Directory.build.props file.

Hopefully in this post you’ve seen one option for switching TFMs and making it easier for developers to work with a multi-targeted solution. If you use multi-targeting in your application or library, it’s work considering adding filtering and conditional TFMs to make development easier.

Xamarin Developer Summit Schedule Breakdown

In just under a month some of the biggest names in the Xamarin community will be presenting alongside a prominent members of the Microsoft Xamarin and Xamarin.Forms teams at the Xamarin Developer Summit.

Xamarin Developer Summit – 11/12 July – Houston, Texas

Whilst I’m not going to be able to make it across to the summit I wanted to do a bit call out to all the great sessions that are in the schedule. The summit is a result of the massive effort that Dan Siegel has put in and the support from all the fantastic presenters.

If you scan the schedule you’ll no doubt be familiar with some of the names but with so many great sessions, how do you know where to start. I thought I’d take the opportunity to break the sessions into some groups to help you decide what’s of interest. These are of course just my groups based on what I can gather from the session description.

Getting Started / Overview

Workshops

Optimisation / Quality

Testing

Mvvm / Architecture

  • Xamarin.Forms made better with Prism – Hussain Abbasi
  • Streamline & Simplify Events with Reactive Extensions – Shane Neuville
  • Rapid Enterprise Architecture Delivery – Paul Schroeder
  • Reactive UI and Reactive Extensions for Xamarin.Forms – Michael Stonis
  • Mobile architecture with MvvmCross, are you doing it right? – Martijn van Dijk
  • Using Xamarin.Forms Shell to easily create a consistent, dynamic, customized, and feature filled UI – Shane Neuville

Security

  • Authentication and Authorization for Xamarin apps using ADB2C and MSAL – David Allen

Device

AI / ML

  • Cognitive Services in Xamarin Applications – Veronika Kolesnikova
  • Build Smarter Cross-Platform Applications Using Xamarin, Azure Cognitive Services, and ML.NET – Richard Taylor
  • How to build your modern ai app with Xamarin – Lo Kinfey

Azure

  • Crafting Real-Time Mobile Apps with SignalR – James Montemagno
  • Light up Xamarin Apps with ChatBots – Sam Basu
  • Build a mobile chatbot with Xamarin & Bot Framework – Luis Beltran
  • Build a Media Streaming App With Azure and Xamarin – Matt Soucoup
  • Create Mixed Reality Experiences with Azure Spatial Anchors and Xamarin – Sweekriti Satpathy

Data

Design

  • Let’s Make Crazy Beautiful UI With Xamarin.Forms – David Ortinau
  • Creating Consistent UI with Xamarin.Forms Visual – David Ortinau

General

DevOps

For more information on any of these topics then you should Register and bring all your learnings back to your company, community through user groups and other forums.

Xamarin and the HttpClient For iOS, Android and Windows

Xamarin and the HttpClient For iOS, Android and Windows

In an earlier post that talked about using dependency injection and registering interfaces for working with Refit across both Prism and MvvmCross I had code that registered an instance of the CustomHttpMessageHandler class which internally used a HttpClientHandler for its InnerHandler. For developers who have spent a bit of time optimising their iOS, Android or Windows application, you’ll have noted that using the HttpClientHandler is generally not deemed to be best practice.  As I’m a big fan of trying to demonstrate best practices, I figured I’d expand on this a little into a post talking about the HttpClient and some of the options you have.

Firstly, a couple of bits of side reading:

What you should gather from these articles is that Microsoft is doing their best to set you up for success but not wanting to take any documentation for granted, let’s see what happens when we create a brand new Xamarin.Forms project and spin up an instance of the HttpClient. When creating the project I just picked the Blank Xamarin.Forms template and made sure that all three platforms were included. The code for creating the HttpClient just uses the zero-parameter constructor:

var client = new HttpClient();

Let’s run each platform and see what the HttpClient gives us (and at this point I haven’t updated any NuGet packages, framework versions or anything. This is just what VS2019 gives me when I create a new XF project).

UWP

image

Here we get the managed HttpClientHandler, rather than the newer (and arguably better) WinHttpHandler. Actually I didn’t find a definitive guide on which is better for UWP, although this stackoverflow post does seem to imply the WinHttpHandler would be the preferred choice, particularly if you want to leverage Http/2.

Android

image

Android is using the AndroidClientHandler which is what should give us the most up to date http experience.

iOS

image

iOS is using the NSUrlSessionHandler which is what should give us the most up to date http experience.

This all seems good (albeit that you might want to use the WinHttpHandler on UWP) so for a lot of developers they might never run into any issues. If you did want to adjust which handler is used on iOS and Android (again assuming you’re just using the HttpClient with the default constructor) you can do so via the properties dialog for the corresponding platform:

image

However, where things come unstuck is if you want to customise some of the http behaviour. In my previous post I demonstrated setting the compression flag but it could have equally been adding an additional header or changing the credentials that are sent as part of each request. In this case, it’s easy enough to use the overload of the HttpClient constructor that takes a HttpMessageHandler and use the managed HttpClientHandler implementation (as I demonstrated). As you’d have seen from the linked articles above, this isn’t ideal as the managed implementation doesn’t leverage the platform specific optimisations.

The better approach is for my application to register the platform specific handler, which in MvvmCross can be done via the Setup class (which is created by default when using MvxScaffolding):

UWP

public class Setup : MvxFormsWindowsSetup<Core.App, UI.App>
{
     protected override void InitializeIoC()
     {
         base.InitializeIoC();
        Mvx.IoCProvider.LazyConstructAndRegisterSingleton<HttpMessageHandler, IServiceOptions>(options =>
         {
return new WinHttpHandler()
             {
                AutomaticDecompression = options.Compression
             };
         });
     }
}

Android

public class Setup : MvxFormsAndroidSetup<Core.App, UI.App>
{
     protected override void InitializeIoC()
     {
         base.InitializeIoC();

 

        Mvx.IoCProvider.LazyConstructAndRegisterSingleton<HttpMessageHandler, IServiceOptions>(options =>
         {
             return new AndroidClientHandler
             {
                 AutomaticDecompression = options.Compression
             };
         });
     }
}

iOS

public class Setup : MvxFormsIosSetup<Core.App, UI.App>
{
     protected override void InitializeIoC()
     {
         base.InitializeIoC();

 

        Mvx.IoCProvider.LazyConstructAndRegisterSingleton<HttpMessageHandler, IServiceOptions>(options =>
         {
             var nsoptions = NSUrlSessionConfiguration.DefaultSessionConfiguration;
             if (options.Compression == System.Net.DecompressionMethods.None)
             {
                 nsoptions.HttpAdditionalHeaders = new NSDictionary("Accept-Encoding", "identity", new object[] { });
             }
             var handler = new NSUrlSessionHandler(nsoptions);
             return handler;
         });
     }
}

Note: for iOS the NSUrlSessionHandler enabled compression by default, so the code here illustrates how you could disable compression if you wanted by sending the identity Accept-Encoding header.

In this post I’ve shown you how you can register each of the native platform handlers to optimise the requests made when using the HttpClient. This post should be read in conjunction with my earlier post that registered the other classes necessary to create the HttpClient based on the registered handler. The only other change is that the HttpService constructor should accept an HttpMessageHandler instead of an ICustomHttpMessageHandler.

public class HttpService : IHttpService
{
     public HttpService(HttpMessageHandler httpMessageHandler, IServiceOptions options)
     {
         HttpClient = new HttpClient(httpMessageHandler as HttpMessageHandler)
         {
             BaseAddress = new Uri(options.BaseUrl)
         };
     }

 

    public HttpClient HttpClient { get; }
}

Update: It’s worth noting that the WinHttpHandler used in the UWP example isn’t part of the core framework. Instead it is accessible via the System.Net.Http.WinHttpHandler NuGet package. Visual Studio provides a handy way to find and install this package – selecting the WinHttpHandler reference (where there is a build error) and looking at the intellisense options, there is an option to Install the System.Net.Http.WinHttpHandler package.

image

Lazy Dependencies and Interfacing Refit with MvvmCross and Prism

Lazy Dependencies and Interfacing Refit with MvvmCross and Prism

Refit is one of those libraries that makes your life as a developer having to write code to work with services that much easier. By way of an example let’s look at the GET List Users method from the sample REST service available at https://reqres.in/

image

Running the response json through JsonToCSharp we get a class model similar to:

public class User
{
     public int id { get; set; }


    public string first_name { get; set; }


    public string last_name { get; set; }


    public string avatar { get; set; }
}


public class UserList
{
     public int page { get; set; }


    public int per_page { get; set; }


    public int total { get; set; }


    public int total_pages { get; set; }


    public List<User> data { get; set; }
}

Next we define the interface for our service (Note that the Get attribute defines the path, including the page parameter that’s passed into the RetrieveUsers method):

public interface IUserService
{
     [Get(“/api/users?page={page}”)]
     Task<UserList> RetrieveUsers(int page);
}

Of course, we need to add a reference to the Refit NuGet package to our application. Lastly we of course need to retrieve an instance of the UserService, which is where the magic comes in – behind the scene, Refit generates the appropriate implementation for the IUserService so that all you need to do is retrieve it using code similar to:

var userService = RestService.For<IUserService>(“https://reqres.in/”);

This is all well and good but when it comes to writing applications using Prism or MvvmCross, what we really want to be able to do is have the IUserService injected into the constructor of our view model.

Injecting Refit with MvvmCross

We’ll start with MvvmCross (I’ve create a brand new project using MvxScaffold to get me started) and I’ve modified the HomeViewModel to add IUserService as a parameter to the constructor. The service is then used in the ViewAppeared method to retrieve the first page of users.

public class HomeViewModel : BaseViewModel
{
     private IUserService UserService { get; }
     public HomeViewModel(IUserService userService)
     {
         UserService = userService;
     }


    public override async void ViewAppeared()
     {
         base.ViewAppeared();


        var users = await UserService.RetrieveUsers(1);
         Debug.WriteLine(users.data.Count);

     }
}

Now the trick is that we need to register the instance of the IUserService with MvvmCross which we can do at the end of the Initialize method of App.cs in the Core project. We don’t want all of the services to be created at start-up, so instead we register the type using the LazyConstructAndRegisterSingleton extension method: For example:

public override void Initialize()
{
     …
     Mvx.IoCProvider.LazyConstructAndRegisterSingleton<IUserService>
         (() => RestService.For<IUserService>(“https://reqres.in/”));
}

There’s a couple of issues here:

– String literal for the host url

– No ability to override the HttpClient used by the IUserService implementation

Let’s see how we can resolve this by registering some other instances that we can use as part of creating the IUserService. One of the other options for the For method is that we can supply a HttpClient that already has a BaseAddress set. We can also set a HttpMessageHandler on the HttpClient instance that will allow us to customise the behaviour of the HttpClient. Unfortunately the only interface that the HttpClient implements is IDisposable. To get around this we wrap the HttpClient instance in a service which returns the instance as a property:

public interface IHttpService
{
     HttpClient HttpClient { get; }
}


public class HttpService : IHttpService
{
     public HttpService(ICustomHttpMessageHandler customHttpMessageHandler, IServiceOptions options)
     {
         HttpClient = new HttpClient(customHttpMessageHandler as HttpMessageHandler)
         {
             BaseAddress = new Uri(options.BaseUrl)
         };
     }
     public HttpClient HttpClient { get; }
}

Note that in this example the HttpService implementation relies on an ICustomHttpMessageHandler and IServiceOptions. These are here to illustrate the chaining of dependencies that can all be lazy loaded.

public interface ICustomHttpMessageHandler
{
}
public class CustomHttpMessageHandler : DelegatingHandler, ICustomHttpMessageHandler
{
     public CustomHttpMessageHandler(IServiceOptions options)
     {
         InnerHandler = new HttpClientHandler()
         {
             AutomaticDecompression = options.Compression
         };
     }
}
public interface IServiceOptions
{
     string BaseUrl { get; }
     DecompressionMethods Compression { get; }
}
public class ServiceOptions : IServiceOptions
{
     public string BaseUrl { get; set; }
     public DecompressionMethods Compression { get; set; }
}

The final registration code in the Initialize method is:

public override void Initialize()
{
     …
     Mvx.IoCProvider.LazyConstructAndRegisterSingleton<IServiceOptions>(() => new ServiceOptions()
     {
         BaseUrl = “https://reqres.in”,
         Compression = DecompressionMethods.Deflate | DecompressionMethods.GZip
     });
     Mvx.IoCProvider.LazyConstructAndRegisterSingleton<ICustomHttpMessageHandler, CustomHttpMessageHandler>();
     Mvx.IoCProvider.LazyConstructAndRegisterSingleton<IHttpService, HttpService>();
     Mvx.IoCProvider.LazyConstructAndRegisterSingleton<IUserService, IHttpService>
         (service => RestService.For<IUserService>(service.HttpClient));
}

Injecting Refit with Prism

I’m not going to cover over generating the classes and IUserService interface. Instead we’re going to look at how we can register and then make use of the IUserService within Prism. One of the limitations of Prism is that there is no way to register a singleton for lazy creation using a function callback. This means we’d need to register an actual implementation of the IUserService which would mean creating the service instance on app startup instead of when the service is first used. Luckily there is a work around that makes use of the Lazy<T> class. Again, the Lazy<T> doesn’t implement an interface, so we’ll inherit from Lazy<T> and implement a new interface ILazyDependency.

public interface ILazyDependency<T>
{
     T Value { get; }
}
public class LazyDependency<T> : Lazy<T>, ILazyDependency<T>
{
     public LazyDependency(Func<T> valueFactory) : base(valueFactory)
     {
     }
}

And now here’s the registration code (added to the RegisterTypes method in App.xaml.cs):

protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
     …
     containerRegistry.RegisterInstance<IServiceOptions>(new ServiceOptions()
     {
         BaseUrl = “https://reqres.in”,
         Compression = DecompressionMethods.Deflate | DecompressionMethods.GZip
     });
     containerRegistry.RegisterSingleton<ICustomHttpMessageHandler, CustomHttpMessageHandler>();
     containerRegistry.RegisterSingleton<IHttpService, HttpService>();
     containerRegistry.RegisterInstance<ILazyDependency<IUserService>>(
         new LazyDependency<IUserService>(() =>
         RestService.For<IUserService>(
             PrismApplicationBase.Current.Container.Resolve<IHttpService>().HttpClient)
         ));
}

This code looks very similar to the registration code when using MvvmCross. However, the difference is that instead of registering IUserService, it registers ILazyDependency<IUserService>. This is important to note because we need to remember to request the correct interface instance in our view model:

public class MainPageViewModel : ViewModelBase
{
     private IUserService UserService { get; }
     public MainPageViewModel(INavigationService navigationService, ILazyDependency<IUserService> userService)
         : base(navigationService)
     {
         UserService = userService.Value;
         Title = “Main Page”;
     }
     public override async void OnNavigatedTo(INavigationParameters parameters)
     {
         base.OnNavigatedTo(parameters);


        var users = await UserService.RetrieveUsers(0);
         Debug.WriteLine(users.data.Count);
     }
}

And there you have it, we’ve abstracted the Refit implementation away from our view models, making them ready for testing.

#The following code was added to the MvvmCross project to simplify the registration of the IUserService:

public static class MvxIoCContainerExtensions
{
     private static Func<TInterface> CreateResolver<TInterface, TParameter1>(
         this IMvxIoCProvider ioc,
             Func<TParameter1, TInterface> typedConstructor)
             where TInterface : class
             where TParameter1 : class
     {
         return () =>
         {
             ioc.TryResolve(typeof(TParameter1), out var parameter1);
             return typedConstructor((TParameter1)parameter1);
         };
     }


    public static void LazyConstructAndRegisterSingleton<TInterface, TParameter1>(this IMvxIoCProvider ioc, Func<TParameter1, TInterface> constructor)
         where TInterface : class
         where TParameter1 : class
     {
         var resolver = ioc.CreateResolver(constructor);
         ioc.RegisterSingleton(resolver);
     }
}

Note: This code has already been merged into MvvmCross but at time of writing isn’t in the stable release.

ViewModel to ViewModel Navigation in a Xamarin.Forms Application with Prism and MvvmCross

ViewModel to ViewModel Navigation in a Xamarin.Forms Application with Prism and MvvmCross

I’m a big fan of the separation that the Mvvm pattern gives developers in that the user interface is encapsulated in the view (Page, UserControl etc) and that the business logic resides in the ViewModel/Model. When structuring the solution for an application I will go so far as to separate out my ViewModels into a separate project from the views, even with Xamarin.Forms where the views can be defined in a .NET Standard library.

One of the abstractions that this lends itself to is what is referred to as ViewModel to ViewModel navigation – rather than the ViewModel explicitly navigation to a page, or bubbling an event up to the corresponding view to get the view to navigate to the next page, ViewModel to ViewModel navigation allows the ViewModel to call a method such as Navigation(newViewModel) where the newViewModel parameter is either the type of the ViewModel to navigate to, or in some frameworks it may be an actual instance of the new ViewModel.

MvvmCross

Let’s see this in action with MvvmCross first – I’m going to start here because ViewModel to ViewModel navigation is the default navigation pattern in MvvmCross. I’ll start with a new project, created using the MvxScaffolding I covered in my previous post, using MvvmCross in a Xamarin.Forms application. The single view template already comes with a page, HomePage, with corresponding ViewModel, HomeViewModel. To demonstrate navigation I’m going to add a second page and a second ViewModel. Firstly, I’ll add a new class, SecondViewModel, which will inherit from BaseViewModel (a class generated by MvxScaffolding which inherits from MvxViewModel that’s part of MvvmCross).

public class SecondViewModel : BaseViewModel
{
}

Next, I’ll add a new ContentPage called SecondPage (note the convention here that there is a pairing between the page and the ViewModel ie [PageName]Page maps to [PageName]ViewModel)

image

MvvmCross supports automatic registration of pages and ViewModels but it does require that the page inherits from the Mvx base class, MvxContentPage. I just need to adjust the root XAML element from

<ContentPage …

to

<views:MvxContentPage x_TypeArguments=”viewModels:SecondViewModel” …

The inclusion of the TypeArguments means that the generic overload of MvxContentPage is used, providing a helpful ViewModel property by which to access the strongly typed ViewModel that is databound to the page.

Now that we have the second page, we just need to be able to navigate from the HomePage. I’ll add a Button to the HomePage so that the user can drive the navigation:

<Button Text=”Next” Clicked=”NextClicked” />

With method NextClicked as the event handler (here I’m just using a regular event handler but in most cases this would be data bound to a command within the HomeViewModel):

private async void NextClicked(object sender, EventArgs e)
{
     await ViewModel.NextStep();
}

And of course we need to add the NextStep method to the HomeViewModel that will do the navigation. The HomeViewModel also needs access to the IMvxNavigationService in order to invoke the Navigate method – this is done by adding the dependency to the HomeViewModel constructor.

public class HomeViewModel : BaseViewModel
{
     private readonly IMvxNavigationService navigationService;


    public HomeViewModel(IMvxNavigationService navService)
     {
         navigationService = navService;
     }
     public async Task NextStep()
     {
         await navigationService.Navigate<SecondViewModel>();
    }
}

As you can see from this example the HomeViewModel only needs to know about the SecondViewModel, rather than the explicit SecondPage view. This makes it much easier to test the ViewModel as you can provide a mock IMvxNavigationService and verify that the Navigate method is invoked.

Prism

Now let’s switch over to Prism and I’ve used the Prism Template Pack to create a new project. To add a second page I’ll add a SecondPageViewModel, which in the case of Prism inherits from ViewModelBase and requires the appropriate constructor that provides access to the INavigationService. Note that the naming convention with Prism is slightly different from MvvmCross where the ViewModel name is [PageName]PageViewModel (ie both the page and the viewmodel have the Page suffix after the PageName eg SecondPage and SecondPageViewModel)

public class SecondPageViewModel : ViewModelBase
{
     public SecondPageViewModel(INavigationService navigationService) : base(navigationService)
     {
     }
}

I’ll add a new ContentPage called SecondPage but unlike MvvmCross I don’t need to alter the inheritance of this page. Instead what I do need to do is register the page so that it can be navigated to. This is done in the App.xaml.cs where there is already a RegisterTypes method – note the additional line to register SecondPage.

protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
     containerRegistry.RegisterForNavigation<NavigationPage>();
     containerRegistry.RegisterForNavigation<MainPage, MainPageViewModel>();
     containerRegistry.RegisterForNavigation<SecondPage, SecondPageViewModel>();
}

Similar to the MvvmCross example, I’ll add a button to the MainPage (the first page of the Prism application created by the template) with code behind to call the NextStep method on the MainViewModel

private async void NextClicked(object sender, EventArgs e)
{
     await (BindingContext as MainPageViewModel).NextStep();
}

Note that because the MainPage just inherits from the Xamarin.Forms ContentPage there’s no property to expose the data bound viewmodel. Hence the casting of the BindingContext, which you’d of course do null checking and error handling around in a real world application.

public async Task NextStep()
{
     await NavigationService.NavigateAsync(“SecondPage”);
}

The NextStep method invokes the NavigateAsync method using a string literal for the SecondPage – I’m really not a big fan of this since it a) uses a string literal and b) requires the the ViewModel knows about the view that’s being navigated to. So let’s adjust this slightly by changing the way that pages and ViewModels are registered. The RegisterForNavigation method accepts a parameter that allows you to override the navigation path, meaning we can set it to be the name of the ViewModel instead of the name of the page.

protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
     containerRegistry.RegisterForNavigation<NavigationPage>();
     containerRegistry.RegisterForNavigation<MainPage, MainPageViewModel>(nameof(MainPageViewModel));
     containerRegistry.RegisterForNavigation<SecondPage, SecondPageViewModel>(nameof(SecondPageViewModel));
}

The navigation methods would then look like:

public async Task NextStep()
{
     await NavigationService.NavigateAsync(nameof(SecondPageViewModel));
}

But I think we an improve this further still by defining a couple of extension methods

public static class PrismHelpers
{
     public static void RegisterForViewModelNavigation<TView, TViewModel>(this IContainerRegistry containerRegistry)
         where TView : Page
         where TViewModel : class
     {
         containerRegistry.RegisterForNavigation<TView, TViewModel>(typeof(TViewModel).Name);
     }


    public static async Task<INavigationResult> NavigateAsync<TViewModel>(this INavigationService navigationService)
         where TViewModel : class
     {
         return await navigationService.NavigateAsync(typeof(TViewModel).Name);
     }
}

Using these extension methods we can update the registration code:

protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
     containerRegistry.RegisterForNavigation<NavigationPage>();
     containerRegistry.RegisterForViewModelNavigation<MainPage, MainPageViewModel>();
     containerRegistry.RegisterForViewModelNavigation<SecondPage, SecondPageViewModel>();
}

And then the navigation code:

public async Task NextStep()
{
     await NavigationService.NavigateAsync<SecondPageViewModel>();
}

The upshot of these changes is that there’s almost no difference between the MvvmCross method of navigation and what can be done with a little tweaking with Prism.

Scaffolding Your Next MvvmCross Xamarin.Forms Project

Scaffolding Your Next MvvmCross Xamarin.Forms Project

One of the things I liked about the getting started experience with Prism was that there was a Visual Studio extension that made creating a new project super simple. Whilst I know that MvvmCross doesn’t provide something like that out of the box, I decided to take a look at some of the project/solution templates that the community have created. The Getting Started page on the MvvmCross website does maintain a list of MvvmCross templates but I must confess that some of these are a little dated. I just went through and opened each of the links, and there were only two that seemed to be recent, Mvx Toolkit and MvxScaffolding. I downloaded both extensions and just happened to try out MvxScaffolding first. Here’s a quick summary of creating a new Xamarin.Forms application using MvxScaffolding

Search for Mvx to find the scaffolding templates

image

Basic project details

image

Now we’re into the MvxScaffolding custom dialog – wow, look at how nice this is.

image

I went with the Single Item template – it’s a tad confusing that you have to click on the grey circle to pick each option, rather than clicking the whole card. Next up, pick the platform and which test projects you want generated.

image

I added UWP support, and asked for all the test projects

image

A quick summary before the projects are created

image

The final solution

image

And of course, the running application.

image

This was a bit mind blowing to be honest – the level of detail in this extension was awesome and I was able to generate the runnable application in under a minute. I like the way the projects are separated and that it can generate all the test projects.

Screenshots for Xamarin.Forms with MvvmCross Article

Screenshots for Xamarin.Forms with MvvmCross Article

A while ago I wrote an article for Visual Studio magazine that details getting started with Xamarin.Forms and MvvmCross. Whilst some things have changed since then, the process for getting started is still basically the same. To verify this process I followed the article and took screenshots along the way – these mostly correspond to the screenshots that are in the article, with a couple of extras thrown in to ensure good coverage of the steps. Please follow the instructions in the article Build a Xamarin.Forms Application with MvvmCross:

Create the new solution

image

Pick the Blank template using the .NET Standard library

Note that the order of the Code Sharing Strategy has changed, placing .NET Standard as the default (about time!!!)

image

Add a new Core library

image

Add reference to the Core library to each project

image

Update existing NuGet packages

image

Add reference to MvvmCross to ALL projects

image

Add reference to MvvmCross.Forms to all EXCEPT the Core project

image

Add App class to Core project and add necessary using statements

image

Update the MainPage to use the MvxContentPage as base class and in Views namespace

image

Don’t forget to update the code-behind file – Remove base class and update namespace

image

UWP: Change MainPage to inherit from MvxFormsWindowsPage and fix using statement

image

UWP: The updated MainPage

image

UWP: The updated MainPage code-behind file

image

UWP: Update the App class to inherit from ProxyMvxApplication

image

UWP: The ProxyMvxApplication class and the App code-behind

Note: I cheated here for the screenshot and had these in the same file. I would recommend having the ProxyMvxApplication in a separate file

image

Android: Error when building

image

Android: Add reference to Mono.Android.Export

image

Android: Change MainActivity to RootActivity and inherit from MvxFormsAppCompatActivity

image

iOS: Update AppDelegate to inherit from MvxFormsApplicationDelegate

image

I hope these screenshots help anyone who runs into issues getting started when following the Visual Studio Magazine article

———-

Contact Built to Roam for more information on building cross-platform applications

———-

Intercepting the Android Software Back Button in Xamarin.Forms

Intercepting the Android Software Back Button in Xamarin.Forms

Recently an issue was raised on MvvmCross that claimed there was an issue intercepting the back button in a Xamarin.Forms + MvvmCross application running on Android. I spent a bit of time investigating the issue as I didn’t believe MvvmCross was doing anything that would prevent an application from intercepting the back button. In this post I’ll walk through my investigation and demonstrate a solution that I came up with. Just for clarity, the issue was referring to intercepting the software back button that appears in the navigation bar.

In the comments someone had referenced a solution proposed by Udara Alwis (https://theconfuzedsourcecode.wordpress.com/2017/03/12/lets-override-navigation-bar-back-button-click-in-xamarin-forms/) which states that the OnOptionsItemSelected method can be overridden in the Activity that hosts the Xamarin.Forms application. Further comments indicated that this solution didn’t work for a Xamarin.Forms application that was using MvvmCross. My starting point was to of course validate this assertion. Using the Playground sample in the MvvmCross source code I attempted to override the OnOptionsItemSelected method in the MainActivity of the Android application. I ran the application and sure enough, the OnOptionsItemSelected method does not get invoked when tapping the software back button.

Having validated that the MvvmCross Playground was indeed broken, I next wanted to test to see if it was an issue with Xamarin.Forms itself. To do this, I started by creating a new Xamarin.Forms application (I’m just going to build for Android, since this is just a sample).

image

The Blank application template comes with a single page, MainPage. I added a second page to the application:

image

Onto the MainPage I added a Button and in the Clicked event handler added code to navigate to the page I just added:

private async void Button_Clicked(object sender, EventArgs e)
{
     await Navigation.PushAsync(new Page1());
}

In order to get the navigation bar to appear, I changed the App constructor to make use of a NavigationPage:

public App()
{
     InitializeComponent();


    MainPage = new NavigationPage(new MainPage());
}

Lastly, I needed to add the OnOptionsItemSelected override to the MainActivity.

public override bool OnOptionsItemSelected(IMenuItem item)
{
     return base.OnOptionsItemSelected(item);
}

Unfortunately, after doing all this, the OnOptionsItemSelected method was still not called. Looks like the issue sits with Xamarin.Forms, so it’s time to start trawling through the source code.

After an initial inspection it appears that the FormsAppCompatActivity does override the OnOptionsItemSelected method, but that doesn’t explain why overriding it in my application wouldn’t work. However, further inspection revealed that the NavigationPageRenderer (the renderer for the NavigationPage) implements the IOnClickListener interface and registers itself with the ActionBar (which corresponds to the navigation bar). Doing this prevents the call to the OnOptionsItemSelected method, which is why we were not seeing it called.

I figured that I would try extending the default renderer and provide my own implementation of the IOnClickListener interface:

[assembly: ExportRenderer(typeof(NavigationPage), typeof(HackBackButton.CustomNavigationPageRenderer))]
namespace HackBackButton
{
     public class CustomNavigationPageRenderer : NavigationPageRenderer, IOnClickListener
     {
         public CustomNavigationPageRenderer()
         {
         }


        public CustomNavigationPageRenderer(Context context) : base(context)
         {
         }


        public new void OnClick(Android.Views.View v)
         {
             Element?.PopAsync();
         }
    }
}

This works and the OnClick method is invoked when the software back button is tapped. Of course, we then need to route this to the current page so that it can decide if it should allow the back navigation.

Turns out that it’s nothing to do with MvvmCross and that the issue lie entirely with Xamarin.Forms. There needs to be a better way to intercept the back navigation. Someone has already listed a issue with Xamarin.Forms, so add a comment and make sure this issue gets some attention.

Getting Started with Xamarin.Forms and Presentation Attributes in MvvmCross

Getting Started with Xamarin.Forms and Presentation Attributes in MvvmCross

Previous posts in this sequence on Getting Started with Xamarin.Forms:
Multi-Targeting in Visual Studio, SDK Versions, MvvmCross, Authenticating with ADAL
Refactoring MvvmCross and Services, Effects, Navigation with MvvmCross

The great thing about MvvmCross is that it provides a minimal effort approach to things like navigation. However, there are times when you want to override the navigation model. For example you might want a page to appear as a modal page instead of a regular page. MvvmCross allows you to do this by overriding the default way that a page is presented, which is controlled by a presentation attribute applied to the page. Out of the box, MvvmCross comes with a number of presentation attributes that you can use to control how a page is rendered. By default, if you attempt to navigate to a page, MvvmCross assumes that you just want to navigate to that page using the Xamarin.Forms PushAsync method. This is the same as if you had attributed the page with the MvxContentPagePresentation attribute e.g.

[MvxContentPagePresentation]
public partial class MainPage

Note: By default you do not need to attribute each page with an attribute if you just want to navigate to the page. You can use the MvxContentPagePresentation attribute to override the back stack (i.e. using a combination of the NoHistory and WrapInNavigation properties)

I’m going to change the ProfilePage to appear as a modal page. The only change I need to make is to add the MvxModalPresentation attribute to the ProfilePage class.

[MvxModalPresentation]
public partial class ProfilePage

Now when I run the application the Profile Page will appear as a modal page. Unfortunately there is currently no way to dismiss the modal page, so the application will get stuck with the ProfilePage displayed. To fix this, let’s add a button to the ProfilePage that will be used to close the modal page. Again, I’ll start by showing how to close the page in code behind using the Xamarin.Forms navigation construct:

<Button Text=”Close” Clicked=”CloseClicked”/>


private async void CloseClicked(object sender, System.EventArgs e)
{
    await Navigation.PopModalAsync();
}

Note: Xamarin.Forms distinguishes between closing a modal page using PopModalAsync vs closing a regular page using PopAsync.

Of course, we’d prefer to have the button linked to a command in the view model that can be used to close the corresponding page. The code for the Button and the ProfileViewModel are as follows.

<Button Text=”Close” Command=”{Binding CloseCommand}”/>

public class ProfileViewModel: MvxNavigationViewModel
{
     private IMvxCommand closeCommand;

    public ProfileViewModel(IMvxLogProvider logProvider, IMvxNavigationService navigationService) : base(logProvider, navigationService)
     {
     }

    public IMvxCommand CloseCommand => closeCommand ?? (closeCommand = new MvxAsyncCommand(()=>NavigationService.Close(this)));
}

Note: The ProfileViewModel inherits from the MvxNavigationViewModel so that it can access the NavigationService.

The NavigationService exposes a Close method which will close the current view model, and the corresponding page. This code is the same irrespective of whether the ProfilePage is shown as a regular or a modal page.

There you have it – you can easily override how pages are displayed in your Xamarin.Forms application using presentation attributes in MvvmCross. There are other attributes that come with MvvmCross to allow you easily display pages in master-detail, tabbed and carousel layouts.

image

In addition to the built in attributes, you can override the MvxPagePresentation attribute and add your own custom layout logic for how pages are presented.

Getting Started with Xamarin.Forms and Navigation with MvvmCross

Getting Started with Xamarin.Forms and Navigation with MvvmCross

Previous posts in this sequence:
Getting Started with Xamarin.Forms and Multi-Targeting in Visual Studio
Getting Started with Xamarin.Forms and SDK Versions
Getting Started with Xamarin.Forms with MvvmCross
Getting Started with Xamarin.Forms and Authenticating with ADAL
Getting Started with Xamarin.Forms, Refactoring MvvmCross and Services
Getting Started with Xamarin.Forms and Effects

Xamarin.Forms has a built in mechanism for navigating between pages. Let’s start with navigating from the MainPage to a new ProfilePage, based on the Content Page item template, added to the Pages folder of the UI project.

image

To trigger the navigation, we’ll add a new Button to the MainPage and add logic to navigate to the ProfilePage.

<Button Text=”Profile” Clicked=”ProfileClicked”/>

private async void ProfileClicked(object sender, System.EventArgs e)
{
     await Navigation.PushAsync(new ProfilePage());
}

Navigation between pages includes adding a back button and handles any hardware back button

imageimage

The limitation with the built in navigation model for Xamarin.Forms is that it is triggered in the UI layer (i.e. in the code behind of the MainPage). Using MvvmCross we can move the navigation logic into the domain of the view model, meaning it can be tested independently of the UI.

To do this, we need to add a ProfileViewModel class, which corresponds to the ProfilePage.

image

The ProfileViewModel should inherit from MvxViewModel:

public class ProfileViewModel: MvxViewModel
{
}

The ProfilePage also needs to be updated to inherit from MvxContentPage.

We can now update the MainViewModel to expose a command that can be used to navigate to the ProfileViewModel:

private IMvxCommand profileCommand;
public IMvxCommand ProfileCommand => profileCommand ?? (profileCommand = new MvxAsyncCommand(ShowProfile));
private async Task ShowProfile()
{
     var navService = Mvx.IoCProvider.Resolve<IMvxNavigationService>();
     await navService.Navigate<ProfileViewModel>();

}

The code in the MainPage can now be changed to connect the button to the ProfileCommand in MainViewModel.

<Button Text=”Profile” Command=”{Binding ProfileCommand}”/>

Navigation is now triggered from the ProfileViewModel.

Refactoring to use MvxNavigationViewModel

The code added to the ShowProfile method uses the Mvx IoC container to look up the IMvxNavigationService. This isn’t great as it is a hidden dependency. An alternative would be to add the IMvxNavigationService as a parameter to the MainPage constructor, which would be injected automatically

public MainViewModel(IMvxNavigationService navigationService, ICredentialService credentialService)

Adding the IMvxNavigationService as a parameter in the constructor means that the view model has to maintain a reference to it until it’s used by the ShowProfile method. Alternatively, we can change the base class to use the MvxNavigationViewModel which already exposes a NavigationService property.

public MainViewModel(IMvxLogProvider logProvider, IMvxNavigationService navigationService, ICredentialService credentialService) : base(logProvider, navigationService)

and the updated ShowProfile method is:

private async Task ShowProfile()
{
     await NavigationService.Navigate<ProfileViewModel>();
}

Navigation using MvvmCross provides a nice abstraction away from the UI layer. It can also be customised to display master-detail, tabbed and other page types.

Getting Started with Xamarin.Forms, Refactoring MvvmCross and Services

Getting Started with Xamarin.Forms, Refactoring MvvmCross and Services

Previous posts in this sequence:
Getting Started with Xamarin.Forms and Multi-Targeting in Visual Studio
Getting Started with Xamarin.Forms and SDK Versions
Getting Started with Xamarin.Forms with MvvmCross
Getting Started with Xamarin.Forms and Authenticating with ADAL

In this post I’m going to do a bit of a refactor based on the work completed in the previous post where we added code directly into the MainViewModel to use the Azure Active Directory Authentication Library. This would be fine if the MainViewModel was the only view model in the application that needed access to the Authenticate method. As I will probably want to make use of it elsewhere, I’ve decided to refactor that code into a class called CredentialService.

When I wired up MvvmCross a couple of posts ago, I did so without going into much detail on how the relationship between view models and their corresponding page is setup. I’m not going to delve into the inner workings of MvvmCross but suffice to say that there is some reflection magic that goes on based on the names of the classes. A view model with the name MainViewModel will be paired with a page called MainPage.

In addition to the magic that glues the view model and pages together (and thus the navigation model) MvvmCross also has a quite powerful dependency injection framework. The CredentialService that I’ll be creating will be registered with the DI framework as a singleton that can be easily accessed.

Let’s start by creating the CredentialService and it’s corresponding interface ICredentialService in a folder called Services in the Core library.

public partial class CredentialService:ICredentialService
{
     private const string AuthEndpoint = “
https://login.microsoftonline.com/{0}/oauth2/token”;


    private const string ResourceId = “https://graph.windows.net/”;
     private const string TenantId = “[your tenantId]”;
     private const string ClientId = “[your clientId]”;
     private const string RedirectURI = “[your redirectURI]”;


    public string AccessToken { get; set; }


    public async Task<string> Authenticate()
     {
         string authority = string.Format(CultureInfo.InvariantCulture, AuthEndpoint, TenantId);
         var authContext = new AuthenticationContext(authority);


        var result = await authContext.AcquireTokenAsync(ResourceId, ClientId, new Uri(RedirectURI), this.PlatformParameters);
         AccessToken = result.AccessToken;
         return AccessToken;
     }
}

public interface ICredentialService
{
     Task<string> Authenticate();
}

The platform specific code will need to be extracted out of the MainViewModel.Platform.cs into the corresponding CredentialService.Platform.cs. For example the UWP class would be:

public partial class CredentialService
{
     private IPlatformParameters PlatformParameters => new PlatformParameters(PromptBehavior.Always, false);
}

In order to register the CredentialService with the DI framework, we just need to add a small bit of code to the Initialize method of the App class in the Core library

public override void Initialize()
{
     CreatableTypes()
         .EndingWith(“Service”)
         .AsInterfaces()
         .RegisterAsLazySingleton();


    RegisterAppStart<MainViewModel>();
}

The last step is to make use of the service in the MainViewModel. We do this by adding a parameter of type ICredentialService to the MainViewModel constructor.

private ICredentialService CredentialService { get; }
public MainViewModel(ICredentialService credentialService)
{
     CredentialService = credentialService;
}


private async Task Authenticate()
{
     Debug.WriteLine(await CredentialService.Authenticate());
}

And that’s it – we haven’t changed any functionality but the solution is better organised and structured in a way that services can easily be added without cluttering up view models.

image

Getting Started with Xamarin.Forms with MvvmCross

Getting Started with Xamarin.Forms with MvvmCross

Previous posts in this sequence:

Getting Started with Xamarin.Forms and Multi-Targeting in Visual Studio

Getting Started with Xamarin.Forms and SDK Versions

Next up is adding in MvvmCross – no surprises here: Add the MvvmCross package to all projects in the solution

image

We need to make a few adjustments to the Core library. First up, we need to add an App class to Core that inherits from MvxApplication. For the moment the only thing this class is going to do is to set the initial view model of the application. One of the core ideas behind MvvmCross is that navigation is driven from view models, rather than at the view level. By setting the initial view model, we’re effectively defining which view will be shown when our application launches.

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

We’ll do a bit of tidying up whilst we’re making changes to the Core project. We’ll create a ViewModels folder and move MainViewModel into folder

We’ll update MainViewModel and MainViewModel.Platform.cs (all four) to add ViewModels to namespace. We’ll also update MainViewModel to inherit from MvxViewModel.

Now that we’ve updated the Core library, we’ll turn our attention to the UI library and the head projects. We’ll add the MvvmCross.Forms NuGet reference to all projects, except Core.

image

The only other change we need to make to the UI library for the moment is to update MainPage to inherit from MvxContentPage instead of ContentPage. You’ll need to do this in both the xaml and xaml.cs files. However, I would recommend dropping the inheritance from the xaml.cs file as it is not required in multiple places.

Next up we need to adjust each of the head projects (UWP, iOS and Android) so that they use MvvmCross to launch the Xamarin.Forms views.

UWP

Rename MainPage to HostPage (in both MainPage.xaml and MainPage.xaml.cs, and the filenames themselves) in the UWP head project – we do this to avoid any confusion between the name of the page and any views that are automatically associated with MainViewModel. Also update HostPage to inherit from MvxFormsWindowsPage.

For UWP we need an additional step due to some limitation around the support for generics. Add a help class ProxyMvxApplication, defined as follows:

public abstract class ProxyMvxApplication : MvxWindowsApplication<MvxFormsWindowsSetup<Core.App, UI.App>, Core.App, UI.App> { }

Change App.xaml and App.xaml.cs to inherit from ProxyMvxApplication, and remove all code in App.xaml.cs other than the constructor which should contain a single call to InitializeComponent. Override the HostWindowsPageType method in the App class

protected override Type HostWindowsPageType()
{
     return typeof(HostPage);
}

Build and run the UWP application

iOS

Update the AppDelegate as follows

public partial class AppDelegate : MvxFormsApplicationDelegate<MvxFormsIosSetup<Core.App, UI.App>, Core.App, UI.App>
{ }

Build and run the iOS application.

Android

Rename and update the MainActivity as follows. Again, we rename the activity to avoid confusion with any activity or view that would be automatically associated with MainViewModel.

public class HostActivity
         : MvxFormsAppCompatActivity<MvxFormsAndroidSetup<Core.App, UI.App>, Core.App, UI.App>
{ }

Build and run the Android application.

You may experience a build error when you attempt to build and run the Android application, relating to referencing the Mono.Android.Export library. In this case, use the Add Reference dialog to manually add the missing reference.

image

There you have it – all three platforms running Xamarin.Forms application powered by MvvmCross.

Speed up development with Multi-Targeted Visual Studio Projects

Speed up development with Multi-Targeted Visual Studio Projects

One of the biggest changes that MvvmCross undertook at the beginning of the year was to switch over to using Multi-targeting in Visual Studio. Instead of having a lot of platform specific projects, multi-targeting made it possible to create a single MvvmCross project that had multiple target frameworks. MvvmCross leverages Oren’s MSBuild.Sdk.Extras nuget package which allows us to define the list of target frameworks using the TargetFrameworks element in the csproj file (note that this is different from the default singular TargetFramework element which usually exists).

<TargetFrameworks>netstandard2.0;net461;Xamarin.iOS10;Xamarin.Mac20;Xamarin.TVOS10;Xamarin.WatchOS10;MonoAndroid81;tizen40;netcoreapp2.0;uap10.0.16299</TargetFrameworks>

Now, instead of ~220 projects, the MvvmCross.sln now has 57 projects. However, this transition comes at a cost. Firstly, defining all the target frameworks like this will mean that it won’t compile on Mac because UWP doesn’t exist there. MvvmCross addressed this by including some conditional logic to detect whether the solution was being built on Windows or not.

<TargetFrameworks Condition=” ‘$(OS)’ == ‘Windows_NT’ “> netstandard2.0;net461;Xamarin.iOS10;Xamarin.Mac20;Xamarin.TVOS10;Xamarin.WatchOS10;MonoAndroid81;tizen40;netcoreapp2.0;uap10.0.16299</TargetFrameworks>
<TargetFrameworks Condition=” ‘$(OS)’ != ‘Windows_NT’ “> netstandard2.0;net461;Xamarin.iOS10;Xamarin.Mac20;Xamarin.TVOS10;Xamarin.WatchOS10;MonoAndroid81;tizen40;netcoreapp2.0</TargetFrameworks>

The second problem is that building the solution became incredibly slow. Every change to the core MvvmCross project meant it had to be recompiled for each target framework. Luckily this doesn’t impact developers using MvvmCross, but it does mean that making changes to MvvmCross is a painful process. This is made worse by the weak support within Visual Studio for multi-targeting, often resulting in having to clean bin/obj folders, restarting VS etc.

Most of the time when working on a feature or a bug in MvvmCross we’ll be targeting one platform; at least until we have it working, and then we’ll test on the other platforms. It would be really nice if Visual Studio supported this workflow, allowing us to effectively disable compilation of the platforms we’re not interested in.

Recently we made a change to the MvvmCross projects to add further conditional logic:

<PropertyGroup Condition=” ‘$(TargetsToBuild)’ == ‘All’ “>
   <TargetFrameworks Condition=” ‘$(OS)’ == ‘Windows_NT’ “>netstandard2.0;net461;Xamarin.iOS10;Xamarin.Mac20;Xamarin.TVOS10;Xamarin.WatchOS10;MonoAndroid81;tizen40;netcoreapp2.0;uap10.0.16299</TargetFrameworks>
   <TargetFrameworks Condition=” ‘$(OS)’ != ‘Windows_NT’ “>netstandard2.0;net461;Xamarin.iOS10;Xamarin.Mac20;Xamarin.TVOS10;Xamarin.WatchOS10;MonoAndroid81;tizen40;netcoreapp2.0</TargetFrameworks>
</PropertyGroup>


<PropertyGroup Condition=” ‘$(TargetsToBuild)’ != ‘All’ “>
   <TargetFrameworks Condition=” ‘$(TargetsToBuild)’ == ‘Android’ “>netstandard2.0;MonoAndroid81;</TargetFrameworks>
   <TargetFrameworks Condition=” ‘$(TargetsToBuild)’ == ‘Uap’ “>netstandard2.0;uap10.0.16299</TargetFrameworks>
   <TargetFrameworks Condition=” ‘$(TargetsToBuild)’ == ‘iOS’ “>netstandard2.0;Xamarin.iOS10</TargetFrameworks>
</PropertyGroup>

As you can see, this inspects the variable TargetsToBuild in order to determine what target platforms to build. This variable is defined in the Directory.build.props so that it can be referenced from any of the MvvmCross projects.

<PropertyGroup Condition=” ‘$(Configuration)’ == ‘Debug’ “>
   <!– <TargetsToBuild>All</TargetsToBuild>  –>
   <!–<TargetsToBuild>Android</TargetsToBuild>–>
   <TargetsToBuild>Uap</TargetsToBuild>
   <!–<TargetsToBuild>iOS</TargetsToBuild>–>
</PropertyGroup>


<PropertyGroup Condition=” ‘$(Configuration)’ != ‘Debug’ “>
   <TargetsToBuild>All</TargetsToBuild>
</PropertyGroup>

To make sure that this option doesn’t affect Release builds (ie creation of nuget packages) if the Configuration isn’t set to Debug, TargetsToBuild is set to All. In the above code, the TargetsToBuild variable is set to Uap. This means that only the Uap (and netstandard) targets will be built – this is a massive performance gain when debugging.

Note: If you change the TargetsToBuild property you will most likely have to restart Visual Studio and/or force a rebuild of the entire solution but the performance gain you get out of using the TargetsToBuild variable is well worth it.

Debugging and Contributing to MvvmCross

Debugging and Contributing to MvvmCross

As one of the maintainers of MvvmCross I quite often help triage issues that are raised on GitHub. Issues are a mixed blessing – it’s really important to us that the community logs any issues that they come across so that we have a mechanism to track and resolve them with each release. However, the flipside is that issues become a dumping ground for developers who use MvvmCross, have come across an issue that they want, or need, fixed – they log an issue and assume that other contributors, or the maintainers, will resolve the issue in a timely manner. Before I get into the body of this post which will drill into how to debug MvvmCross, as well as how to proactively contribute to MvvmCross, I want to reiterate the point that open source software, which includes MvvmCross, ships as-is. This means whatever expectations you have of it working, or working a specific way, or any expectations you have on how issues will get resolved, or how quickly they’ll get resolved, are wrong. The only things you really know for certain is that there is source code that’s available for anyone to access and contribute to, and there are some versions of the library that are available via NuGet so that they can be easily consumed by developers. Of course, with this said, the MvvmCross team, like other OSS, do strive to address issues as quickly as they can but we all have day jobs and lives that mean our contribution to OSS is limited. In this post I will be covering what you can do when submitting an issue to make it easy for someone to resolve your issue as quickly as possible.

Debugging with MvvmCross using Source Linking

Note: Source Linking doesn’t work with all platform. If you building a Xamarin.Forms application, it’s worth creating a UWP head just in case you need to debug an MvvmCross issue.

We often get issues raised, either via GitHub, Slack or Stack overflow, that start along the lines of “my application doesn’t start (or crashes) when I do …..” Because the issue often resides within a commercial product that the developer is working on, we don’t typically ask developers to share their solution. Instead what we typically ask is that they use the Playground sample that’s included with the MvvmCross source code and see if they can reproduce the issue.

However, before getting to this point, there is a step that can often help identify the cause of any issue, which is to setup and use source linking to step through the MvvmCross source code. Source Linking is a technique that’s been around for quite some time and is something that MvvmCross has setup to allow developers to easily step from their code, directly into the source code of MvvmCross without any complex setup process.

Getting source linking to work isn’t always as easy as it should be. There are a few websites that cover how to enable source linking (such as this post by Cameron Taggart) and they all basically say the same thing, which is that to enable source linking you need to check the Options >> Debugging >> Enable Source Link support checkbox in Visual Studio.

Update: Whilst you’ve in Options, you can uncheck the “Enable Just My Code” as this setting will mask any internal exceptions that may be thrown during start-up. In the current version (6.1.2) there is an issue whereby exceptions raised during the start-up sequence in the Setup class are swallowed, preventing start-up completing. This will manifest differently on each platform but for a Xamarin.Forms application it may result in a white-screen being displayed on start-up and never proceeding to the first page of the application. Unchecking this option will mean that VS will break each time an exception is raised, allowing you to see the cause of the exception.

image

However, what’s not covered is what do you do if this doesn’t work. I don’t have a definitive answer either but I did find that after I toggled a few of the options (eg Enable source server support) I was able to get source linking to work.

Once you’ve got Source Linking enabled, how do you use it? There are a couple of options but they amount to the same thing – you need to trigger some debugging action that will allow you to step through the source code. The first option, and one that will work if something is going wrong within MvvmCross that may be triggering an exception that’s handled/swallowed. In this case you’ll need to change your Exception Settings so that all Common Language Runtime Exceptions cause Visual Studio to Break. After changing this setting, if an exception is raised within MvvmCross, you’ll be able to step through the code at the point the Exception is raised.

 image

The next option is to put a breakpoint in a method that overrides a method defined within MvvmCross. For example in the ProxyMvxApplication class, which inherits form MvxApplication, we can override the ReigsterSetup method and place a breakpoint in the method.

image

When the breakpoint is hit, you can see in the call stack that there are methods in the MvvmCross library.

image

Double-clicking on one of these lines will trigger the source code to be downloaded so that you can then step through the code. However, before the source code is downloaded, you may see the following prompt:

image

At this point you can opt in or out of source linking for this debugging session. Once you agree to Download Source and Continue Debugging, the cursor will be taken to the appropriate line of code and you can continue your debugging session.

image

As you can imagine, being able to step through the MvvmCross code means that you can try to identify what might be causing the issue you’re seeing. It’ll also mean that should you need to log a GitHub issue, you can provide us with a much more detailed description of what’s going on.

Contributing to MvvmCross

Contributing to OSS can happen at various levels, mainly depending on the amount of time someone has available and their inclination to help the community. For most developers their first contribution to MvvmCross will be in the form of a new issue. When submitting an issue, it’s important that you select the most appropriate issue template as a starting point.

image

As a community we welcome all feedback and encourage developers to raise an appropriate issue. Whilst we will respond to “Questions and Help” issues, we’d prefer it if you reached out via Slack or StackOverflow and actually talk to us!

It’s important to remember that in order to follow up on an issue we need as much information as possible. For example, information on how to reproduce an issue is invaluable. Providing a series of steps on how to reproduce an issue is a good starting point, and will help reduce the amount of time it takes to try to work out what’s going on. Alternatively, instead of just listing steps, a better way to demonstrate an issue is to use the Playground sample that’s included within the MvvmCross source code and reproduce the issue there. Once you’re able to reproduce the issue, you can submit a PR for review which may form the basis for resolving the issue.

Quick Steps for Resolving Issues:

  • In your solution: enable all exceptions in the Exception Settings window of Visual Studio – this will point out if an internal exception is occurring
  • In your solution: enable source linking where possible – this will allow you to step through MvvmCross source to try to work out what’s going wrong
  • In the Playground sample app in MvvmCross: attempt to reproduce the issue
  • Ask on Slack (or Stackoverflow) for assistance from the MvvmCross community
  • Raise an issue on the MvvmCross GitHub
  • Submit a PR with the changes to the Playground sample that demonstrates the issue

Note: When submitting a PR, please mark the PR as work in progress by putting [WIP] at the beginning of the title. This will prevent the PR from accidentally being merged. Also, please link the PR to the issue so that it’s easy to understand the purpose of the PR.

You might be asking why I’m asking that you submit a PR. It’s for two reasons:

1) I’m lazy – when I read your issue, it requires quite a bit of effort for me to reproduce the issue. If you provide a PR, I can simply pull your branch, run up the Playground and see the issue for my self. Not only that, I can also commit changes directly on your branch which means I can fix the issue straight away.

2) I want you to join the MvvmCross community – you only really join the community when you start contributing code to the project. By getting you to submit a PR, you’re breaking the ice and getting involved. The PR is a WIP so there’s no judgment from the community; in fact they’ll be impressed that you’re willing to get involved. We’ll often reach out directly to you and discuss the issue.

The upshot is that by submitting a PR you have a much higher chance of getting your issue looked at #NoGuarantees

MVX=1F: TipCalc with Xamarin.Forms (MVX+1 days of MvvmCross)

MVX=1F: TipCalc with Xamarin.Forms (MVX+1 days of MvvmCross)

In this post I’m going to extend the TipCalc to include Xamarin.Forms targets, similar to what we did in the post MVX=0F: A first MvvmCross Application (MVX+1 days of MvvmCross)

Adding Xamarin.Forms

Note: The following instructions can be applied to any project by simply replacing TipCalc with the name of your project

  1. Add a New Project based on the Mobile App (Xamarin.Forms) template 
    image

  2. In the New Cross Platform App dialog, select Blank App, check the Platforms you want, select .NET Standard and click OK
    image
  3. Upgrade the Xamarin.Forms NuGet to latest for all four Forms projects
  4. Add MvvmCross NuGet Package to all Forms projects (Forms, Forms.iOS, Forms.Android and Forms.UWP)
  5. Add MvvmCross.Forms NuGet Package to all Forms projects (Forms, Forms.iOS, Forms.Android and Forms.UWP)

Update the TipCalc.Forms project

  1. Remove all code in App class except for constructor with a call to InitializeComponent
  2. Create Views folder
  3. Move MainPage into Views folder and rename to FirstView
  4. Adjust FirstView.xaml and FirstView.xaml.cs to change class name to FirstView and to make it inherit from MvxContentPage

Update the FirstDemo.Forms.Uwp project

  1. Update Microsoft.NETCore.UniversalWindowsPlatform
  2. Add reference to TipCalc.Core
  3. Change MainPage to inherit from MvxFormsWindowsPage
  4. Remove all code other than the InitializeComponent method call in the constructor of MainPage
  5. Add ProxyMvxApplication
    public abstract class ProxyMvxApplication : MvxWindowsApplication<MvxFormsWindowsSetup<Core.App, TipCalc.Forms.App>, Core.App, TipCalc.Forms.App, MainPage>
  6. Change App.xaml and App.xaml.cs to inherit from ProxyMvxApplication
  7. Remove all code other than the constructor, with a single call to InitializeComponent, in App.xaml.cs

Update the FirstDemo.Forms.Android project

Note (1): If you run into the following error, you may need to rename your project. In this case we renamed it to Forms.Droid (as well as the folder the project resides in)
1>C:Program Files (x86)Microsoft Visual StudioPreviewEnterpriseMSBuildXamarinAndroidXamarin.Android.Common.targets(2088,3): error MSB4018: System.IO.PathTooLongException: The specified path, file name, or both are too long. The fully qualified file name must be less than 260 characters, and the directory name must be less than 248 characters.

Note (2): If you’re using the preview build of Visual Studio, you may run into an error: “error XA4210: You need to add a reference to Mono.Android.Export.dll when you use ExportAttribute or ExportFieldAttribute.” If you do, you just need to Add Reference to Mono.Android.Export (search in the Add Reference dialog).

  1. Change Forms.Android project to target latest Android SDK
  2. Upgrade Xamarin.Android.Support.* to latest for the Forms.Android project
  3. Add reference to TipCalc.Core
  4. Change MainActivity inheritance, remove code except for a constructor:
    public class MainActivity : MvxFormsAppCompatActivity<MvxFormsAndroidSetup<Core.App, App>, Core.App, App>
    {
    }

Update the TipCalc.Forms.iOS project

  1. Add reference to TipCalc.Core
  2. Changed inheritance of AppDelegate
    public partial class AppDelegate : MvxFormsApplicationDelegate<MvxFormsIosSetup<Core.App, TipCalc.Forms.App>, Core.App, TipCalc.Forms.App>

Adding TipCalc Xamarin.Forms Layout

Update the TipCalc.Forms project by updating the FirstView.xaml with the following XAML.

<?xml version=”1.0″ encoding=”utf-8″ ?>
<views:MvxContentPage
    
     http://xamarin.com/schemas/2014/forms"”>http://xamarin.com/schemas/2014/forms”
              http://schemas.microsoft.com/winfx/2009/xaml"”>http://schemas.microsoft.com/winfx/2009/xaml”
              x_Class=”TipCalc.Forms.Views.FirstView”>
  
  <StackLayout>
         <Label Text=”SubTotal”/>
         <Editor Text=”{Binding SubTotal, Mode=TwoWay}”/>
         <Label Text=”How generous?”/>
         <Slider Value=”{Binding Generosity, Mode=TwoWay}”
                     Minimum=”0″
                     Maximum=”100″/>
         <Label Text=”Tip:”/>
         <Label Text=”{Binding Tip}”/>
         <Label Text=”SubTotal:”/>
         <Label Text=”{Binding Total}”/>
     </StackLayout>

</views:MvxContentPage>

That’s it – there’s nothing more to do in order to add Xamarin.Forms targets (iOS, Android and UWP) to the TipCalc

MVX+1 Update

MVX+1 Update

MvvmCross v6.0.1 was recently released. I’ve just updated both FirstDemo and TipCalc to reference v6.0.1 of MvvmCross

One of the changes that I did make to all projects is how packages are referenced. By default in Visual Studio when you reference a NuGet package it will draw in the specific version. However, by editing the csproj you can set the version to * which will mean Visual Studio will draw in the latest stable version of your referenced libraries. This is particularly convenient if you’re not in the habit of remembering to upgrade packages frequently. The downside is that you may discover one day that your app stops working, or behaves differently, thanks to a new package version being used by your application. More on this in a future post once I’ve collected my current thinking regarding continous deployment and the impact this would have on app development

MVX=0 WPF/Mac: A first MvvmCross Application (MVX+1 days of MvvmCross)

MVX=0 WPF/Mac: A first MvvmCross Application (MVX+1 days of MvvmCross)

Further to some feedback on the first couple of posts in the MVX+1 series (MVX=0, MVX=0F and MVX=1), I’ve gone back and added WPF and Mac support to the FirstDemo to show just how powerful MvvmCross is. I’ll cover very briefly here how to add these project types and then I’ll be updating the TipCalc post to include the basics for WPF and Mac setup in there too.

Let’s start with WPF:

  1. Add a WPF App (.NET Framework) called FirstDemo.Wpf
    image
  2. Add a reference to MvvmCross NuGet package
  3. Add reference to FirstDemo.Core project
  4. Add ProxyMvxApplication to App.xaml.cs
    public abstract class ProxyMvxApplication : MvxApplication<MvxWpfSetup<Core.App>, Core.App> { }
  5. Update App class in both App.xaml and App.xaml.cs to inherit from ProxyMvxApplication
  6. Remove all code in the App class (in App.xaml.cs) except for the constructor with call to InitializeComponent
    public App()
    {
         InitializeComponent();
    }
  7. Update MainWindow in both MainWindow.xaml and MainWindow.xaml.cs to inherit from MvxWindow
  8. Create Views folder
  9. Add new User Control (WPF) called FirstView.xaml
  10. Update FirstView in both FirstView.xaml and FirstView.xaml.cs to inherit from MvxWpfView
  11. Add XAML to FirstView.xaml
    <StackPanel Margin=”12,12,12,12″>
         <TextBox Text=”{Binding FirstName, Mode=TwoWay}”></TextBox>
         <TextBox Text=”{Binding LastName, Mode=TwoWay}”></TextBox>
         <TextBlock Text=”{Binding FullName}”></TextBlock>
    </StackPanel>

You should now be able to build and run the WPF application – notice how little code we have to add/change in order to get MvvmCross to work!

Next up is Mac which at the moment can only be done on a Mac (almost no surprises there – it’s the same as not being able to do UWP development on a Mac I guess):

  1. Add a Cocoa App called FirstDemo.Mac
    image
  2. Add a reference to MvvmCross NuGet package
  3. Add reference to FirstDemo.Core project
  4. At this point I would recommend unloading your project, editing the csproj file manually to removed the legacy nuget reference and add the following ItemGroup. Also remove the reference to package.config, remove the actual package.config file and delete both bin and obj folders. Reload the Mac project and force a rebuild.
    <ItemGroup>
       <PackageReference Include=”MvvmCross” Version=”6.0.0″ />
    </ItemGroup>
  5. Update AppDelegate to inherit from MvxApplicationDelegate
    [Register(“AppDelegate”)]
    public class AppDelegate: MvxApplicationDelegate<MvxMacSetup<App>, App>
    {
         public override void DidFinishLaunching(NSNotification notification)
         {
             MvxMacSetupSingleton.EnsureSingletonAvailable(this, MainWindow).EnsureInitialized();
             RunAppStart();
            // Due to a bug, do not call base DidFinishLaunching
             //base.DidFinishLaunching(notification);
         }
    }
    Note: There is a bug at the moment where if you don’t override DidFinishLaunching the application will crash on startup
  6. Open main.storyboard – this should open the storyboard in the XCode designer
  7. Select the existing view controller and set the Class and Storyboard ID to FirstView
    image
  8. Make sure the FirstView name change has been saved; return to VS for Mac and confirm that files FirstView.cs and FirstView.designer.cs have been created. If these have not been created, I would suggest doing a rebuild of your project to make sure the changes to the storyboard have been processed and the appropriate designer files created.
  9. From the Object Library drag two TextField and a Label across onto the design surface
    image
  10. Open Project navigator and make sure you have FirstView.h and FirstView.m. If you don’t I would suggest closing both XCode and VS for Mac, deleting the bin and obj folder from within the FirstDemo.Mac folder and reopening the solution in VS for Mac. Rebuild the Mac project and then launch XCode by double clicking on main.storyboard.
    image
  11. Hide both Project navigator (left pane) and Utilities (right pane) and show the Assistant Editor (the icon button that has linked circles in top right corner of XCode)
  12. From the navigator bar in the Assistant Editor, select Manual –> FirstView.Mac –> FirstView.h
    image
  13. Right-click on the first TextField
    image
  14. Click and drag the circle next to New Referencing Outlet across onto the FirstView.h immediately before the @End. When you release, you should be prompted to complete information about the new Outlet where you can specify the Name, textEditFirst, which will be the name of the backing variable you’ll be able to use to reference the TextField in code
    image
  15. Repeat the previous step for the second TextField, textEditSecond, and the Label, labelFull.
  16. Update FirstView.cs to add the MvxFromStoryboard attribute, change the base class to MvxViewController and add data binding using the CreateBindingSet extension
    [MvxFromStoryboard(“Main”)]
    public partial class FirstView : MvxViewController<FirstViewModel>
    {
         public FirstView(IntPtr handle) : base(handle)
         {
         }

  17.     public override void ViewDidLoad()
         {
             base.ViewDidLoad();
             var set = this.CreateBindingSet<FirstView, FirstViewModel>();
             set.Bind(textEditFirst).To(vm => vm.FirstName);
             set.Bind(textEditSecond).To(vm => vm.LastName);
             set.Bind(labelFull).To(vm => vm.FullName);
             set.Apply();
         }
    }

And there you have it – build and run your Mac FirstDemo application

    MvvmCross: Initialize method on the first view model

    MvvmCross: Initialize method on the first view model

    An issue was raised on MvvmCross following the release of v6 where applications were breaking because they have async code in the Initialize method of the first view model. Changes in v6 mean that the first view model is effectively loaded synchronously. This change was made because applications should not do heavy lifting before displaying the first view of their application as there are platform requirements that will cause the application to terminate if it doesn’t display the first view within the appropriate time window. This change means that any code that attempts to jump to a different thread (even something like Task.Delay) will cause the fist view of the application to break.

    In some scenarios this doesn’t make sense – for example if you have a splash screen Activity on Android, the first navigation can be done asynchronously because you already have a view being displayed on the screen. Here I’m going to use a simple example to demonstrate how to resolve this issue – you should only use this if you are using a splash screen for the first view of your application.

    Let’s start by breaking our application by adding the following to our first view model:

    public override async Task Initialize()
    {
         await Task.Delay(2000);
         await base.Initialize();
    }

    If you make this change and attempt to run your application you’ll see a black screen (on Android – and I’m using Android because we typically have a splash Activity such as in my earlier post).

    Next we’re going to fix this by overriding the MvxAppStart class to make the first navigation asynchronous:

    public class CustomMvxAppStart<TViewModel> : MvxAppStart<TViewModel>
         where TViewModel : IMvxViewModel
    {
         public CustomMvxAppStart(IMvxApplication application, IMvxNavigationService navigationService) : base(application, navigationService)
         {
         }


        protected override void NavigateToFirstViewModel(object hint)
         {
             NavigationService.Navigate<TViewModel>();
         }
    }

    Note that it’s the bold line that we’ve changed from the built in MvxAppStart class where we call NavigationService.Navigate<TViewModel>().GetAwaiter().GetResult(); which forces the navigation to be done synchronously.

    We now need to make sure that our CustomMvxAppStart is used. To do this we just need to change a single line in the App.cs to use RegisterCustomAppStart instead of RegisterAppStart

    RegisterCustomAppStart<CustomMvxAppStart<FirstViewModel>>();

    And we’re done – running the application now will allow the application to load successfully.

    MVX=1: TipCalc – a second example – adding IoC and the Xamarin Android Designer (MVX+1 days of MvvmCross)

    MVX=1: TipCalc – a second example – adding IoC and the Xamarin Android Designer (MVX+1 days of MvvmCross)

    Following on from the first post in the MVX+1 series, in this post we’ll create a basic Tip Calculator (mirroring the original post from the N+1 series). However, in this case the first section is a more detailed set of instructions on how to get the basics of all three platforms setup with MvvmCross. Going forward we’ll use this as a starting point so that we don’t need to cover over this in each subsequent post.

    Source code: https://github.com/nickrandolph/MvxPlus1DaysOfMvvmCross/tree/master/Mvx-01-TipCalc

    Getting Started Instructions (Native Apps)

    Note: In this case the name of the application is TipCalc but these instructions can be followed to get any new project started by simply replacing TipCalc with the name of your project.

    1. Create a the new solution by creating a new Class Library (.NET Standard) with the project name set to TipCalc.Core and the solution name to just TipCalc.
    2. Add a new project based on the Blank App (Universal Windows) project template called TipCalc.Uwp (make sure that the Minimum and Target versions are set to at least the Fall Creators Update)
    3. Add a new project based on the Android App (Xamarin) project template called TipCalc.Droid (use the Blank app template)
    4. Add a new project based on the iOS App (Xamarin) project template called TipCalc.iOS (use the Blank app template)
    5. Add a reference to MvvmCross NuGet package (v6.0.0 at time of writing to all four projects)
    6. TipCalc.Uwp: Update NuGet package Microsoft.NETCore.UniversalWindowsPlatform to the latest stable version (6.0.8 at time of writing)
    7. TipCalc.Droid: Update the Android version to Use Latest Platform (Project Properties –> Application –> Target Framework)
    8. TipCalc.Droid: Update Xamarin.Android.Support.Design to latest stable version (27.0.2 at time of writing)
    9. TipCalc.Droid: Add a reference to MvvmCross.Droid.Support.V7.AppCompat package
    10. TipCalc.iOS: Unload project; delete packages.config; edit TipCalc.iOS.csproj and add the following ItemGroup
      <ItemGroup>
         <PackageReference Include=”MvvmCross” Version=”6.0.0″ />
      </ItemGroup>

    11. Add a reference to TipCalc.Core to each of the three head projects (ie TipCalc.Uwp, TipCalc.Droid and TipCalc.iOS)
    12. TipCalc.Core: Rename the default Class1.cs to App.cs, and allow Visual Studio to rename class to App
    13. TipCalc.Core: Change the App class to inherit from MvxApplication
    14. TipCalc.Core: Add a folder, ViewModels, and add a class called FirstViewModel.
    15. TipCalc.Core: Change FirstViewModel to inherit from MvxViewModel
    16. TipCalc.Core: Override Initialize method in App to register services and set startup view model to FirstViewModel
      public override void Initialize()
      {
           CreatableTypes()
                   .EndingWith(“Service”)
                   .AsInterfaces()
                   .RegisterAsLazySingleton();
           RegisterAppStart<FirstViewModel>();
      }

    17. TipCalc.Uwp: Add a help class ProxyMvxApplication

      public abstract class ProxyMvxApplication: MvxApplication<MvxWindowsSetup<Core.App>, Core.App> { }
    18. TipCalc.Uwp: Change App.xaml and App.xaml.cs to inherit from ProxyMvxApplication
    19. TipCalc.Uwp: Remove all code in App.xaml.cs other than the constructor which should contain a single call to InitializeComponent
    20. TipCalc.Uwp: Delete MainPage.xaml and MainPage.xaml.cs
    21. TipCalc.Uwp: Add a Views folder, and  add a FirstView based on the Blank Page template
    22. TipCalc.Uwp: Change FirstView.xaml and FirstView.xaml.cs to inherit from MvxWindowsPage
    23. TipCalc.Droid: Add a new class, MainApplication, that inherits from MvxAppCompatApplication
    24. [Application]
      public class MainApplication : MvxAppCompatApplication<MvxAppCompatSetup<App>, App>
      {
           public MainApplication(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
           {
           }
      }

    25. TipCalc.Droid: Rename MainActivity.cs to SplashScreen.cs and let Visual Studio rename the class
    26. TipCalc.Droid: Rename activity_main.axml to SplashScreen.axml, and adjust layout to indicate application loading
    27. TipCalc.Droid: Adjust SplashScreen class to inherit from MvxSplashScreenAppCompatActivity and set NoHistory to true (since we don’t want the user to be able to press the back button and go back to the splash screen)

      [Activity(Label = “@string/app_name”, Theme = “@style/AppTheme”, MainLauncher = true, NoHistory = true)]
      public class SplashScreen : MvxSplashScreenAppCompatActivity

    28. TipCalc.Droid: Add a folder, Views, and add a new Activity, FirstView.c
    29. [Activity(Label = “FirstView”)]
      public class FirstView : MvxAppCompatActivity<FirstViewModel>
      {
           protected override void OnCreate(Bundle bundle)
           {
               base.OnCreate(bundle);


              SetContentView(Resource.Layout.FirstView);
           }
      }

    30. TipCalc.Droid: Add a new Android Layout to the Resources/Layout folder, FirstView.axml
    31. TipCalc.Droid: You may run into an error: “error XA4210: You need to add a reference to Mono.Android.Export.dll when you use ExportAttribute or ExportFieldAttribute.” If you do, you just need to Add Reference to Mono.Android.Export (search in the Add Reference dialog).
    32. TipCalc.iOS: Update AppDelegate class to inherit from MvxApplicationDelegate, and remove the default code.
      [Register(“AppDelegate”)]
      public class AppDelegate : MvxApplicationDelegate<MvxIosSetup<App>, App>
      {
      }
    33. TipCalc.iOS: Add an Empty Storyboard, called Main.storyboard, to the root of the project
    34. TipCalc.iOS: Add a ViewController to the Main.storyboard using the designer and set the Class and Storyboard ID to FirstView (also make sure the “User Storyboard ID” checkbox is set to true)
    35. TipCalc.iOS: Move the generated FirstView.cs and FirstView.designer.cs files (from the previous step) into a new folder called Views, and adjust the namespace of the FirstView class to FirstView.iOS.Views
    36. TipCalc.iOS: Update the FirstView class to inherit from MvxViewController
      [MvxFromStoryboard(“Main”)]
      public partial class FirstView : MvxViewController<FirstViewModel>

    Now to actually build out the Tip Calculator. Rather than embed the calculation logic into our view model, we’re going to abstract it out into a service. To do this we’ll make use of the IoC container made available by MvvmCross. We’ll register a CalculationService which will be injected into our view model constructor.

    Let’s continue our development of the Tip Calculator in TipCalc.Core:

    1. Add a folder called Services
    2. Add an interface ICalculationService into the Services folder
      public interface ICalculationService
      {
           double Tip(double subTotal, double generosity);
      }
    3. Add a class, CalculationService, which implements ICalculationService, again into the Services folder
      public class CalculationService : ICalculationService
      {
           public double Tip(double subTotal, double generosity)
           {
               return subTotal * generosity / 100.0;
           }
      }
    4. Add a constructor to the FirstViewModel which accepts an ICalculationService parameter
    5. private readonly ICalculationService _calculationService;
      public FirstViewModel(ICalculationService calculationService)
      {
           _calculationService = calculationService;
      }

    6. Add properties for SubTotal, Generosity, Tip and Total. Each property should take the following form where SetProperty is called within the setter
      private double _subTotal;
      public double SubTotal
      {
           get { return _subTotal; }
           set { SetProperty(ref _subTotal, value); }
      }
    7. Add a Recalc method which will invoke the Tip method on the ICalculationService
      private void Recalc()
      {
           Tip = _calculationService.Tip(SubTotal, Generosity);
           Total = SubTotal + Tip;
      }
    8. Add a call to Recalc into the setter for both SubTotal and Generosity
    9. Set some initial values for the SubTotal and Generosity (if you use the property setters, rather than setting the fields, the Recalc method will be invoked)

    That’s it for the core logic for the application. Now we just need to wire up the UI for each platform.

    One thing that’s worth noting is that in step 16 of the original setup where the Initialize method is overridden, there is logic in the Initialize method to interrogate the current assembly looking for all classes that end in Service and register them, based on their interface, with the MvvmCross IoC container – this is how MvvmCross knows about the implementation of the ICalculationService which is used when instantiating the FirstViewModel.

    Let’s built out the UWP interface in TipCalc.Uwp:

    1. Add the following XAML inside the existing Grid element:
      <StackPanel>
           <TextBlock Text=”SubTotal”/>
           <TextBox Text=”{Binding SubTotal, Mode=TwoWay}”/>
           <TextBlock Text=”How generous?”/>
           <Slider Value=”{Binding Generosity, Mode=TwoWay}”
                   Minimum=”0″
                   Maximum=”100″/>
           <TextBlock Text=”Tip:”/>
           <TextBlock Text=”{Binding Tip}”/>
           <TextBlock Text=”SubTotal:”/>
           <TextBlock Text=”{Binding Total}”/>
      </StackPanel>

    That’s the UWP interface done – four elements (with TextBlock headings): TextBox and Slider for inputting SubTotal and Generosity using two-way data binding, and two TextBlock for outputting the Tip and Total amounts.

    Now let’s do Android:

    1. Add the following namespace declaration to FirstView.axml
    2. Add the following xml to FirstView.axml
      <TextView
           android_text=”SubTotal”
           android_textAppearance=”?android:attr/textAppearanceMedium”
           android_layout_width=”fill_parent”
           android_layout_height=”wrap_content”
           android_id=”@+id/textView1″ />
      <EditText
           android_layout_width=”fill_parent”
           android_layout_height=”wrap_content”
           android_id=”@+id/editText1″
           local:MvxBind=”Text SubTotal” />
      <TextView
           android_text=”Generosity”
           android_textAppearance=”?android:attr/textAppearanceMedium”
           android_layout_width=”fill_parent”
           android_layout_height=”wrap_content”
           android_id=”@+id/textView2″ />
      <SeekBar
           android_layout_width=”fill_parent”
           android_layout_height=”wrap_content”
           local:MvxBind=”Progress Generosity”
           android_id=”@+id/seekBar1″ />
      <TextView
           android_text=”Tip”
           android_textAppearance=”?android:attr/textAppearanceMedium”
           android_layout_width=”fill_parent”
           android_layout_height=”wrap_content”
           android_id=”@+id/textView3″ />
      <TextView
           android_textAppearance=”?android:attr/textAppearanceMedium”
           android_layout_width=”fill_parent”
           android_layout_height=”wrap_content”
           local:MvxBind=”Text Tip”
           android_id=”@+id/textView4″ />
      <TextView
           android_text=”Total”
           android_textAppearance=”?android:attr/textAppearanceMedium”
           android_layout_width=”fill_parent”
           android_layout_height=”wrap_content”
           android_id=”@+id/textView5″ />
      <TextView
           android_textAppearance=”?android:attr/textAppearanceMedium”
           android_layout_width=”fill_parent”
           android_layout_height=”wrap_content”
           local:MvxBind=”Text Total”
           android_id=”@+id/textView6″ />

    In this case we’re leveraging the xml extensions offered by MvvmCross in order to do the data binding using the MvxBind syntax.

    Finally, let’s do iOS:

    1. Add the following code to the FirstView.cs
      public override void ViewDidLoad()
      {
           base.ViewDidLoad();

    2.     // Perform any additional setup after loading the view
           var label = new UILabel(new RectangleF(10, 0, 300, 40));
           label.Text = “SubTotal”;
           Add(label);


          var subTotalTextField = new UITextField(new RectangleF(10, 40, 300, 40));
           Add(subTotalTextField);


          var label2 = new UILabel(new RectangleF(10, 80, 300, 40));
           label2.Text = “Generosity?”;
           Add(label2);


          var slider = new UISlider(new RectangleF(10, 120, 300, 40));
           slider.MinValue = 0;
           slider.MaxValue = 100;
           Add(slider);


          var label3 = new UILabel(new RectangleF(10, 160, 300, 40));
           label3.Text = “Tip”;
           Add(label3);


          var tipLabel = new UILabel(new RectangleF(10, 200, 300, 40));
           Add(tipLabel);


          var label4 = new UILabel(new RectangleF(10, 240, 300, 40));
           label4.Text = “Total”;
           Add(label4);


          var totalLabel = new UILabel(new RectangleF(10, 280, 300, 40));
           Add(totalLabel);


         var set = this.CreateBindingSet<FirstView, FirstViewModel>();
           set.Bind(subTotalTextField).To(vm => vm.SubTotal);
           set.Bind(slider).To(vm => vm.Generosity);
           set.Bind(tipLabel).To(vm => vm.Tip);
           set.Bind(totalLabel).To(vm => vm.Total);
           set.Apply();

      }

    In this case for iOS we again make use of the data binding support provided by MvvmCross by using the CreateBindingSet method, followed by a call to Bind for each element property we want to bind, and then finally a call to Apply to complete the setup of data binding.

    And that’s it – a Tip Calculator for all three platforms.

    MVX=0F: A first MvvmCross Application (MVX+1 days of MvvmCross)

    MVX=0F: A first MvvmCross Application (MVX+1 days of MvvmCross)

    Updated 16/4/2018: Reference to MvvmCross have been updated to v6.0.0

    One of the awesome things about MvvmCross is that you can take the exact same set of ViewModels that you used for your UWP, iOS, Android applications and use them in a Xamarin Forms application. As we go through the MVX+1 series, I’ll make some space for talking about Xamarin Forms as well.

    In this post we’re going to extent what we started in the previous post by adding a Xamarin Forms interface – this means creating new head projects for iOS, Android and UWP too. All up we’re adding four new projects but fear not, most of the heavy lifting is done by Visual Studio:

    Into our existing solution we’re going to add a new Xamarin Forms project:

    1. Add a new Mobile App (Xamarin.Forms) project called FirstDemo.Forms
      image_thumb1_thumb
    2. When prompted, select Blank App, make sure all three Platforms are checked and make sure you select the .NET Standard option (Do NOT use the Share Project option).
      image_thumb3_thumb
    3. Upgrade the Xamarin.Forms NuGet to latest for all four Forms projects (at time of writing is 2.5.1.444934)
    4. Add MvvmCross NuGet Package to all Forms projects (Forms, Forms.iOS, Forms.Android and Forms.UWP)
    5. Add MvvmCross.Forms NuGet Package to all Forms projects (Forms, Forms.iOS, Forms.Android and Forms.UWP)

    Note: You may need to upgrade to latest NuGet packages for all Forms projects before you can add references to the MvvmCross packages.

    Update the FirstDemo.Forms project

    1. Remove all code in App class except for constructor with a call to InitializeComponent
    2. Create Views folder
    3. Move MainPage into Views folder and rename to FirstView
    4. Adjust FirstView.xaml and FirstView.xaml.cs to change class name to FirstView and to make it inherit from MvxContentPage
    5. Add two Entry and a Label with data binding to FirstView.xaml

    Update the FirstDemo.Forms.Uwp project

    1. Update Microsoft.NETCore.UniversalWindowsPlatform (v6.0.8 at time of writing)
    2. Add reference to FirstDemo.Core
    3. Change MainPage to inherit from MvxFormsWindowsPage
    4. Remove all code other than the InitializeComponent method call in the constructor of MainPage
    5. Add ProxyMvxApplication
      public abstract class ProxyMvxApplication : MvxWindowsApplication<MvxFormsWindowsSetup<Core.App, FirstDemo.Forms.App>, Core.App, FirstDemo.Forms.App, MainPage>
    6. Change App.xaml and App.xaml.cs to inherit from ProxyMvxApplication
    7. Remove all code other than the constructor, with a single call to InitializeComponent, in App.xaml.cs

    Update the FirstDemo.Forms.Android project

    Note (1): If you run into the following error, you may need to rename your project. In this case we renamed it to Forms.Droid (as well as the folder the project resides in)
    1>C:Program Files (x86)Microsoft Visual StudioPreviewEnterpriseMSBuildXamarinAndroidXamarin.Android.Common.targets(2088,3): error MSB4018: System.IO.PathTooLongException: The specified path, file name, or both are too long. The fully qualified file name must be less than 260 characters, and the directory name must be less than 248 characters.

    Note (2): If you’re using the preview build of Visual Studio, you may run into an error: “error XA4210: You need to add a reference to Mono.Android.Export.dll when you use ExportAttribute or ExportFieldAttribute.” If you do, you just need to Add Reference to Mono.Android.Export (search in the Add Reference dialog).

    1. Change Forms.Android project to target latest Android SDK (v8.1 at time of writing)
    2. Upgrade Xamarin.Android.Support.* to latest for the Forms.Android project
    3. Add reference to FirstDemo.Core
    4. Change MainActivity inheritance, remove code except for a constructor:
      public class MainActivity : MvxFormsAppCompatActivity<MvxFormsAndroidSetup<Core.App, App>, Core.App, App>
      {
      }

    Update the FirstDemo.Forms.iOS project

    1. Add reference to FirstDemo.Core
    2. Changed inheritance of AppDelegate
      public partial class AppDelegate : MvxFormsApplicationDelegate<MvxFormsIosSetup<Core.App, FirstDemo.Forms.App>, Core.App, FirstDemo.Forms.App>

    And there you have it – you should be able to build and run all three Forms projects utilising the same view models that the non-Forms projects used