Dependency Injection

Make the IServiceProvider of your MAUI application accessible with the MVVM CommunityToolkit

Make the IServiceProvider of your MAUI application accessible with the MVVM CommunityToolkit

As you might know, I am in the process of converting all my internal used libraries to be .NET MAUI compatible. This is quite a bigger task than initially thought, although I somehow enjoy the process. One thing I ran pretty fast into is the fact that you can’t access the MAUI app’s IServiceProvider by default.

Possible solutions

As always, there is more than one solution. While @DavidOrtinau shows one approach in the WeatherTwentyOne application that accesses the platform implementation of the Services, I prefer another approach that uses, in fact, Dependency Injection to achieve the same goal.

Implementation

I am subclassing the Microsoft.Maui.Controls.Application to provide my own, overloaded constructor where I inject the IServiceProvider used by the MAUI application. Within the constructor, I am using the MVVM CommunityToolkit’s Ioc.Default.ConfigureServices method to initialize the toolkit’s Ioc handler. Here is the code:

using CommunityToolkit.Mvvm.DependencyInjection;

namespace MauiTestApp
{
	public class MyMauiAppImpl : Microsoft.Maui.Controls.Application
	{
		public MyMauiAppImpl(IServiceProvider services) 
		{
            Ioc.Default.ConfigureServices(services);
        }
	}
}

Usage

Using the class is straight forward. Open your App.xaml file and replace the Application base with your MyMauiAppImpl:

<local:MyMauiAppImpl
             xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:MauiTestApp"
             x:Class="MauiTestApp.App">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="Resources/Styles/Colors.xaml" />
                <ResourceDictionary Source="Resources/Styles/Styles.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</local:MyMauiAppImpl>

And, of course, the same goes for the code behind-file App.xaml.cs:

namespace MauiTestApp;

public partial class App : MyMauiAppImpl
{
	public App(IServiceProvider serviceProvider) : base(serviceProvider)
	{
		InitializeComponent();

		MainPage = new AppShell;
	}

}

That’s it, you can now use the MVVM CommunityToolkit’s Ioc.Default implementation to access the registered Services, ViewModels and Views.

Conclusion

In this post, I showed you a simple (and even easily reusable way) of making the IServiceProvider of your .NET MAUI application available. I also linked to an alternative approach, if you do not want to subclass the application object, I recommend that way.

As always, I hope this post is helpful for some of you.

Until the next post, happy coding, everyone!
Posted by msicc in Dev Stories, MAUI, Xamarin, 1 comment
Invoke platform code in a MAUI app using the built-in Dependency Injection

Invoke platform code in a MAUI app using the built-in Dependency Injection

In Xamarin.Forms, my internal libraries for MVVM helped me to keep my applications cleanly structured and abstracted. I recently started the process of porting them over to .NET MAUI. I was quickly reaching the point where I needed to invoke platform specific code, so I read up the documentation.

The suggested way

The documentation suggests creating a partial class with partial method definitions and a corresponding partial classes with the partial method implementations (like described in the docs). I tried to follow the above-mentioned MAUI documentation and copy/pasted the code sample in there and thought everything is going to be fine. Well, it was not. I wasn’t even able to compile the solution with that code on my Mac in the first place.

In search for a possible cause of this, I did not find a solution immediately. In the end, it turned out that I needed to implement the partial class method on all platforms specified in the TargetFrameworks within the .csproj file. It should have been obvious due to the fact that MAUI is a single project with multiple target frameworks, but it wasn’t on that day.

On top of that, Visual Studio did some strange changes to the .csproj file specifying unnecessary None, Compile and Include targets that should not be generated explicitly, which added a lot to my confusion as well. After removing them from the project file and adding an implementation for all platforms, I was able to compile and test the code from the docs.

But I love my interfaces!

Likewise, that’s why I did not stop there. Following the abstraction approach, interfaces allow us to define the common surface of the API without worrying about the implementation details. That’s not the case for the partial classes approach, like the problems I had showed.

Luckily for us, .NET MAUI supports multi targeting. This means an interface can have a platform specific implementation while being defined in the shared part of the application. If you have used the MSBuildExtras package before, you know already how that works. Best part – .NET MAUI already provides the multi targeting configuration out of the box.

Show me some code!

First, let’s define a simple interface for this exercise:

namespace MAUIDITest.InterfaceDemo
{
    public interface IPlatformDiTestService
    {
        string SayYourPlatformName();
    }
}

Now we are going to implement the platform specific implementations. Go to the first platforms folder and add a new class named PlatformDiTestService. Then – and this is really important to make it work – adjust the namespace to be the same as the one of the interface. Last, but not least, implement the interface, for example like this:

namespace MAUIDITest.InterfaceDemo
{
    public class PlatformDiTestService : IPlatformDiTestService
    {
        public string SayYourPlatformName()
        {
            return "I am MacOS!";
        }
    }
}

Repeat this for all platforms, and replace the platform’s name accordingly.

Using Dependency Injection in MAUI

If you have been following along my past blog posts, you know that I recently switched to the CommunityToolkit MVVM (read more here and here). I already heard that MAUI will get the same DI container built-in, so the choice was obvious. Now let’s have a look how easy we can inject our interface into our ViewModel. Head over to your MauiProgram.cs file and update the CreateMauiApp method:

	public static MauiApp CreateMauiApp()
	{
		var builder = MauiApp.CreateBuilder();
		builder
			.UseMauiApp<App>()
			.ConfigureFonts(fonts =>
			{
				fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
				fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
			});

		//lowest dependency
		builder.Services.AddSingleton<IPlatformDiTestService, PlatformDiTestService>();
		//relies on IPlatformDiTestService
		builder.Services.AddTransient<MainPageViewModel>();
		//relies on MainPageViewModel
		builder.Services.AddTransient<MainPage>();

		return builder.Build();
	}

First, add the registration of the interface and the implementation. In the sample above, the MainPageViewModel relies on the interface and gets it automatically injected by the DI handler. For testing purposes, I even inject the MainViewModel into the MainPage‘s constructor. This is very likely to change in a real world application.

For completeness, here is the MainPageViewModel class:

using System;
using System.ComponentModel;
using System.Windows.Input;
using MAUIDITest.InterfaceDemo;

namespace MAUIDITest.ViewModel
{
    public class MainPageViewModel : INotifyPropertyChanged
    {
        private readonly IPlatformDiTestService _platformDiTestService;
        private string sayYourPlatformNameValue = "Click the 'Reveal platform' button";
        private Command _sayYourPlatformNameCommand;

        public event PropertyChangedEventHandler PropertyChanged;


        public MainPageViewModel(IPlatformDiTestService platformDiTestService)
        {
            _platformDiTestService = platformDiTestService;
        }

        public void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        public string SayYourPlatformNameValue
        {
            get => sayYourPlatformNameValue;
            set
            {
                sayYourPlatformNameValue = value;
                OnPropertyChanged(nameof(this.SayYourPlatformNameValue));
            }
        }

        public Command SayYourPlatformNameCommand => _sayYourPlatformNameCommand ??=
            new Command(() => { this.SayYourPlatformNameValue = _platformDiTestService.SayYourPlatformName(); });
    }
}

And of course, you want to see the MainPage.xaml.cs file as well:

using MAUIDITest.InterfaceDemo;
using MAUIDITest.ViewModel;

namespace MAUIDITest;

public partial class MainPage : ContentPage
{
    private readonly IPlatformDiTestService _platformDiTestService;
    int count = 0;

	public MainPage(MainPageViewModel mainPageViewModel)
	{
		InitializeComponent();

		this.BindingContext = mainPageViewModel;
    }

	private void OnCounterClicked(object sender, EventArgs e)
	{
		count++;

		if (count == 1)
			CounterBtn.Text = $"Clicked {count} time";
		else
			CounterBtn.Text = $"Clicked {count} times";

		SemanticScreenReader.Announce(CounterBtn.Text);
	}
}

Last, but not least, the updated MainPage.xaml file:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="MAUIDITest.MainPage">
			 
    <ScrollView>
        <VerticalStackLayout 
            Spacing="25" 
            Padding="30,0" 
            VerticalOptions="Center">

            <Image
                Source="dotnet_bot.png"
                SemanticProperties.Description="Cute dot net bot waving hi to you!"
                HeightRequest="200"
                HorizontalOptions="Center" />
                
            <Label 
                Text="Hello, World!"
                SemanticProperties.HeadingLevel="Level1"
                FontSize="32"
                HorizontalOptions="Center" />
            
            <Label 
                Text="Welcome to .NET Multi-platform App UI"
                SemanticProperties.HeadingLevel="Level2"
                SemanticProperties.Description="Welcome to dot net Multi platform App U I"
                FontSize="18"
                HorizontalOptions="Center" />

            <Button 
                x:Name="CounterBtn"
                Text="Click me"
                SemanticProperties.Hint="Counts the number of times you click"
                Clicked="OnCounterClicked"
                HorizontalOptions="Center" />


            <Label Text="Test of built in DI:" FontSize="Large" HorizontalOptions="Center"></Label>
            <Label x:Name="PlatformNameLbl" FontSize="Large" HorizontalOptions="Center" Text="{Binding SayYourPlatformNameValue}" />

            <Button Text="Reveal platform" HorizontalOptions="Center" Command="{Binding SayYourPlatformNameCommand}"/>

        </VerticalStackLayout>
    </ScrollView>
 
</ContentPage>

I did not change the default code that comes with the template. You can easily recreate this by using the default MAUI template of Visual Studio and copy/paste the code snippets above to play around with it.

Conclusion

I only started my journey to update my internal libraries to .NET MAUI. I stumbled pretty fast with that platform invoking code, but luckily, I was able to move along. Platform specific code can be handled pretty much the same as before, which I hope I was able to show you in this post. I’ll write more posts on my updating experiences to MAUI as they happen.

Until the next post, happy coding, everyone!
Posted by msicc in Dev Stories, MAUI, Xamarin, 4 comments
Using Microsoft’s Extensions.DependencyInjection package in (Xamarin.Forms) MVVM applications (Part 2)

Using Microsoft’s Extensions.DependencyInjection package in (Xamarin.Forms) MVVM applications (Part 2)

The Key

Our goal is to add keyed registrations to the IServiceCollection, so we need a common denominator to build upon. As I was able to use a string with the SimpleIoc implementation of MVVMLight for years now, I decided to move on with that and created the following, very complex interface:

public interface IViewModelKey
{
    string Key { get; set; }
}

Every ViewModel that should be registered by Key needs to implement that interface from now on in my MVVM environment.

The Resolver

Back in the MVVMLight times, I was able to query the SimpleIoc registrations with the key I was searching for. In the Microsoft.Extensions.DependencyInjection world, things get a bit more complex. While there are different ways to solve the problem (there are some libraries extending the IServiceProvider with additional methods out there, for example), I decided to use the IServiceProvider itself and go down the resolver interface/implementation road.

Let’s have a look at the interface first:

public interface IViewModelByKeyResolver<T> where T : IViewModelKey
{
    public T GetViewModelByKey(string key);
}

Nothing too special here, just a generic implementation of the resolver interface with the requirement of the IViewModelKey implementation from above. This makes the usage pretty straight forward. The more important part here is the implementation, though. Let’s have a look at mine:

public class ViewModelByKeyResolver<T> : IViewModelByKeyResolver<T> where T : IViewModelKey
{
    private readonly IServiceProvider _serviceProvider;

    public ViewModelByKeyResolver(IServiceProvider serviceProvider)
        => _serviceProvider = serviceProvider;

    public T GetViewModelByKey(string key)
        => _serviceProvider.GetServices<T>().SingleOrDefault(vm => vm.Key == key);
}

The registration of the implementation will automatically inject the IServiceProvider instance at runtime for me here. The GetViewModelByKey method searches all registrations of the given type for the key and returns the desired instance.

Registering the Resolver and keyed ViewModels

The registration of the resolver is done like all the other registrations:

this.ServiceDescriptors.TryAddSingleton<IViewModelByKeyResolver<KeyedViewModel>, ViewModelByKeyResolver<KeyedViewModel>>();

Replace KeyedViewModel with your individual type that implements your key interface. That’s it.

For the registration of the KeyedViewModel instances, there is one thing to pay attention to, though. You cannot use the TryAdd{Lifetime} methods here for registration. Instead, just use the Add{Lifetime} method to register them. Here is a sample:

this.ServiceDescriptors.AddSingleton<KeyedViewModel>(new KeyedViewModel("Key1"));
this.ServiceDescriptors.AddSingleton<KeyedViewModel>(new KeyedViewModel("Key2"));
this.ServiceDescriptors.AddSingleton<KeyedViewModel>(new KeyedViewModel("Key3"));
this.ServiceDescriptors.AddSingleton<KeyedViewModel>(new KeyedViewModel("Key4"));
this.ServiceDescriptors.AddSingleton<KeyedViewModel>(new KeyedViewModel("Key5"));

If you know the keyed ViewModels already at the time of your app startup, you can add them right away and create the IServiceProvider instance as shown in my first post. In most cases, however, you will know the information of the keyed instances only at runtime. Luckily, my Xamarin.Forms implementation already has the solution built in. Here is a short reminder:

public ServiceCollection? ServiceDescriptors { get; private set; }

private IServiceProvider? _services;

public IServiceProvider? Services => _services ??= BuildServiceProvider();

public IServiceProvider? BuildServiceProvider(bool resetExisiting = false)
{
    if (this.ServiceDescriptors == null)
        throw new ArgumentNullException($"Please register your Services and ViewModels first with the {nameof(RegisterServices)} and {nameof(RegisterViewModels)} methods.");

    if (resetExisiting)
        _services = null;

    if (_services == null)
        _services = ServiceDescriptors.BuildServiceProvider();

    return _services;
}

The BuildServiceProvider method has an additional parameter that allows to reset the existing IServiceProvider. This way, I can keep my existing registrations and just add the new keyed ones dynamically. Please note that you may need to reinitialize your already registered and used ViewModels under certain circumstances after performing the reset.

Accessing a keyed ViewModel

Last but not least, I need to show you how to access a ViewModel by its key. Luckily, this is not that hard:

KeyedViewModel vm4 = IocManager.Current.Services.GetService<IViewModelByKeyResolver<KeyedViewModel>>().GetViewModelByKey("Key4");
KeyedViewModel vm2 = IocManager.Current.Services.GetService<IViewModelByKeyResolver<KeyedViewModel>>().GetViewModelByKey("Key2");

Conclusion

By switching to the CommunityToolkit.MVVM package and utilizing Microsoft’s Extension.DependencyInjection package together with it, my MVVM environment is ready for upcoming challenges like .NET MAUI. I will be able to use it on all .NET platforms and just need to adapt my Xamarin.Forms implementation to others (which I have done already for one of our internal tools at work in WPF). Even keyed ViewModel instance can be used similarly as before, as I showed you in this post.

As always, I hope this post will be helpful for some of you.

Until the next post, happy coding!

Posted by msicc in Dev Stories, Xamarin, 1 comment
Using Microsoft’s Extensions.DependencyInjection package in (Xamarin.Forms) MVVM applications (Part 1) [Updated]

Using Microsoft’s Extensions.DependencyInjection package in (Xamarin.Forms) MVVM applications (Part 1) [Updated]

As some of you might remember, I was always a big fan of the MVVMLight toolkit. As the later one is now deprecated and MAUI around, I took a look at the CommunityToolkit.Mvvm, which is officially the successor to MVVMLight.

As stated in the documentation of the new Toolkit, one could now use the Microsoft’s Extensions.DependencyInjection package for anything related to Inversion of Control (which used to be handled by the SimpleIoc implementation of MVVMLight before). Because this is also the built-in way for .NET 6 and web applications, I decided to adapt it already now for my Xamarin.Forms apps (especially my new one I am currently working on).

[Update] Nuget packages

Please note that while the toolkit’s source is now separated from the Windows CommunityToolkit, the documentation isn’t. This can be confusing (as it was for me). On top of that, there are now two Toolkit MVVM packages:

I thought I got it right when writing this blog post initially. After Brandon Minnick from Microsoft pointed me to the right package, I realized I was not. Up on further research, I found also this discussion in the GitHub repo, stating the one and only will be the CommunityToolkit package. Please use only this one if you are following my tutorials here. I updated all mentions of the Toolkit in this post accordingly.

Default IServiceProvider implementation

The toolkit has a default implementation for the IServiceProvider provided by the Extension.DependencyInjection package. You can read about it here in the documentation and see the source here on GitHub. It focuses heavily on thread safety, its usage is pretty strict, and it does not allow adding ViewModels dynamically. If you do not need stuff like this in your app, you’re probably fine using the Ioc.Default implementation of the toolkit.

Custom IServiceprovider implementation

In TwistReader, the application I am currently working on, I had my requirements easily resolved by the SimpleIoc implementation of the MVVMLight toolkit. With the Extensions.DependencyInjection package, I had to move on with a custom implementation, on which we will have a deeper look in this post. Before you move on reading, make sure you have read the documentation.

IIocManagerBase interface

Of course, I wanted my custom implementation to be reusable. So I extended my existing base interface that my applications need to implement:

public interface IIocManagerBase 
{
    IServiceProvider? Services { get; }
    ServiceCollection? ServiceDescriptors { get; }

    IServiceProvider? BuildServiceProvider(bool resetExisiting = false);

    void Initialize(bool useDefaultNavigationService = true);

    void RegisterServices(bool useDefaultNavigationService);

    void RegisterViewModels();
}

I added the ServiceDescriptors property as well as an IServiceProvider property including a method to (re)build the ServiceProvider if needed. Let’s continue by having a look at the Xamarin.Forms base implementation.

FormsIocManagerBase base class

Building up on the interface before, I created a base implementation for my Xamarin.Forms apps. Let’s go a bit into the details.

In the Initialize method, I am just calling the RegisterServices and the RegisterViewModels methods. One important thing to notice is that I am instantiating the ServiceDescriptors property in the RegisterViewModels method. I also add my default services already to collection there. The RegisterViewModels method remains empty in the base implementation.

public virtual void Initialize(bool useDefaultNavigationService = true)
{
    RegisterServices(useDefaultNavigationService);
    RegisterViewModels();
}

public virtual void RegisterServices(bool useDefaultNavigationService)
{
    this.ServiceDescriptors = new ServiceCollection();

    this.ServiceDescriptors.TryAddSingleton<IDialogService>(DependencyService.Get<IDialogService>());
    this.ServiceDescriptors.TryAddSingleton<IActionSheetService>(new ActionSheetService());

    if (useDefaultNavigationService)
        this.ServiceDescriptors.TryAddSingleton<INavigationService>(new NavigationService());
    else
        System.Diagnostics.Debug.WriteLine("***** DON'T FORGET TO REGISTER YOUR INavigationService INSTANCE(S)!  *****");
}

public virtual void RegisterViewModels()
{
}

Until I switched to CommunityToolkit.Mvvm, this was all I had in there (using SimpleIoc for service registrations). Now that I am using the Extensions.DependencyInjection package, there is some more work to do:

public ServiceCollection? ServiceDescriptors { get; private set; }

private IServiceProvider? _services;

public IServiceProvider? Services => _services ??= BuildServiceProvider();

public IServiceProvider? BuildServiceProvider(bool resetExisiting = false)
{
    if (this.ServiceDescriptors == null)
        throw new ArgumentNullException($"Please register your Services and ViewModels first with the {nameof(RegisterServices)} and {nameof(RegisterViewModels)} methods.");

    if (resetExisiting)
        _services = null;

    if (_services == null)
        _services = ServiceDescriptors.BuildServiceProvider();

    return _services;
}

The code is not that complex, but helps with the IServiceProvider instance handling. The BuildServiceProvider method has a reset flag that allows me to rebuild the provider at runtime. One scenario where we can use this one is for adding ViewModel registrations dynamically during the runtime of our app, but.

IocManager in-app implementation

The next code block shows a typical in-app implementation of my IocManager. You may have noticed I am using the TryAdd{Lifetime} methods already before when adding items to the ServiceCollection. This makes sure that I have always just one registration and does not throw an exception if I try to add it again. If you prefer the exception, just switch to the Add{Lifetime} version.

public class IocManager : FormsIocManagerBase
{
    private static IocManager _instance;

    public static IocManager Current => _instance ??= new IocManager();


    public override void Initialize(bool useDefaultNavigationService = true)
    {
        base.Initialize(useDefaultNavigationService);
    }

    public override void RegisterServices(bool useDefaultNavigationService)
    {
        base.RegisterServices(useDefaultNavigationService);

        this.ServiceDescriptors.TryAddSingleton<ITestService, TestService1>();
        this.ServiceDescriptors.TryAddScoped<ITestService, TestedTestService>();
    }

    public override void RegisterViewModels()
    {
        this.ServiceDescriptors.TryAddSingleton<MainViewModel>();
        this.ServiceDescriptors.TryAddSingleton<SecondaryViewModel>();
    }

    public MainViewModel MainVm => this.Services.GetRequiredService<MainViewModel>();
    public SecondaryViewModel SecondaryVm => this.Services.GetRequiredService<SecondaryViewModel>();
}

For my Xamarin.Forms applications, I always use the IocManager implementation as a singleton. This makes it pretty easy with the different lifetimes on all platforms. As you can see, there is nothing complicated in the registration process, I just add both my services and my ViewModels to the ServiceCollection.

I also have some convenience properties for the most important ViewModels that make Binding easier (as I tend to keep code behind files as clean as possible). If you need a service in another place in your app, and you are not using constructor injection (which gets automatically resolved by the Microsoft.Extensions.DepedencyInjection package), you can get the instance in the same way as I do with the ViewModel instances above.

Conclusion

Creating a custom IServiceProvider implementation is not that hard. The custom implementation allows one to recreate the IServiceProvider (handle with care!) if needed. In the next post, I will show you how to deal with keyed ViewModel instances when using the Extensions.DependencyInjection package.

Have you already used the Microsoft.Extensions.DependencyInjection package with Xamarin.Forms or other platforms (not web)? What are your experiences? If so, leave a comment or chat with me on Twitter!

As always, I hope this post is helpful for some of you.

Until the next post – happy coding, everyone!

Posted by msicc in Dev Stories, Xamarin, 4 comments
Xamarin Forms, the MVVMLight Toolkit and I: loading bundled assets in the Forms project

Xamarin Forms, the MVVMLight Toolkit and I: loading bundled assets in the Forms project

The scenario …

The reason I came up with this is that I am writing on an Xamarin.Forms web reader app. It is an app that uses a  WebView to display the contents of web articles. Of course, I am using a CSS-file to style the content that gets displayed. I am using the default font of every platform, plus some platform specific settings in there. The easiest way to get it working right is to give every platform its own CSS-file. In the Xamarin.Forms project however, I just want to call one method that gets the thing done.

For this scenario, the well documented Files access in Xamarin.Forms does not work.

This post will not yet be reflected in my ongoing XfMvvmLight project on Github as I have another one building on top of this in my queue. Once the second one is written, the project will show these changes, too. This post will contain the full classes however, so you could C&P them if you want/need.

DependencyService and another interface

If you are following this series already, you might already know that the easiest way to achieve my goal is to use the built-in Xamarin.FormsDependencyService and the needed interface with the native implementations.

So let’s start with the interface:

namespace XfMvvmLight.Abstractions
{
    public interface IAssetPathHelper
    {
        string GetResourceFolderPath(string folderName, bool forWeb = false);

        string GetResourcePath(bool forWeb = false);

        string GetResourceFilePath(string folder, string fileName, bool forWeb = false);
    }
}

The interface dictates three string-returning methods that will either return the base path of the platform resources, a specific folder or the full path to the bundled file. This interface covers most usage scenarios I came across. Feel free to leave any feedback if I am missing out a common one.

The only thing left to do is to register the interface in our ViewModelLocator, like we did already before in the RegisterServices() method:

var assetPathHelper = DependencyService.Get();
SimpleIoc.Default.Register(()=> assetPathHelper);

We are getting the platform implementation via the built in DependecyService and assign in to our Xamarin.Forms interface (like we have done already before). By registering it with our SimpleIoc instance, we can now use it wherever we want in our Xamarin.Forms project.

Platform implementations

Android

If you add files in the Resources folder, you can easily access them via the Resource class in your Android project. However, files like CSS-files are normally placed within the Assets folder of your Xamarin.Android project.

Depending on the usage scenario, we have two ways to access the files in the ‘Assets’ folder. If we are residing in the Xamarin.Android project and want to access the content of those bundled assets, we are able to access them using the Android.App.Context.Assets property and assign it to the Android.Content.Res.AssetManager class. We can then use streams to get the data contained in those files.

This does not help however if we want to access those files from a WebView (both in the Android and the Xamarin.Forms project), that’s why we have to use the ‘file:///android_asset‘ uri-scheme. Here is the platform implementation:

using XfMvvmLight.Abstractions;
using XfMvvmLight.Droid.PlatformImplementation;
using System.Diagnostics;
using System.IO;

[assembly: Xamarin.Forms.Dependency(typeof(AssetPathHelper))]
namespace XfMvvmLight.Droid.PlatformImplementation;
{
    public class AssetPathHelper : IAssetPathHelper
    {
        public string GetResourceFolderPath(string folderName, bool forWeb = false)
        {
            return Path.Combine(GetResourcePath(),folderName);
        }

        public string GetResourcePath(bool forWeb = false)
        {
            //reminding ourselves to double check if this way is really necessary
            if (!forWeb)
            {
                Debug.WriteLine("**********************************");
                Debug.WriteLine("You should consider using AssetManager if you are not using this in a WebView.");
                Debug.WriteLine("See: https://developer.xamarin.com/guides/android/application_fundamentals/resources_in_android/part_6_-_using_android_assets/");
                Debug.WriteLine("**********************************");
            }
            
            //but we are always returning the uri scheme 
            return $"file:///android_asset"; 
        }

        public string GetResourceFilePath(string folder, string fileName, bool forWeb = false)
        {
            var folderPath = string.IsNullOrEmpty(folder) ? GetResourcePath() : GetResourceFolderPath(folder);

            return Path.Combine(folderPath,fileName);
        }
    }
}

The implementation is pretty straight forward. Although we could call all three methods, the one we use probably the most is the GetResourceFilePath method. It will give us the complete path to the resource file, which we can then use in our calling code of our Xamarin.Forms project.

By using the Path.Combine method we make sure we get a valid file path string, which is exactly what we need if we are accessing assets in this way. As most of the scenarios for accessing assets could be easily covered by using AssetManager (see above), I am printing a reminder message that it exists to the output window of VisualStudio.

Important: you have to make sure the Build Action of your files is set to AndroidAsset, otherwise you’ll see nothing, in some scenarios it will even throw exceptions. This accounts for the AssetManager as well as for the AssetPathHelper implementations.

iOS

On iOS, we are able to access bundled assets via the NSBundle class. The implementation is even easier than the one for Android, as this is the only way to get those assets. That’s why we are ignoring the forWeb parameter in this case. Here is the implementation:

using System.IO;
using Foundation;
using XfMvvmLight.Abstractions;
using XfMvvmLight.iOS.PlatformImplementation;

[assembly: Xamarin.Forms.Dependency(typeof(AssetPathHelper))]
namespace XfMvvmLight.iOS.PlatformImplementation
{
    //forWeb is ignored on iOS!
    public class AssetPathHelper : IAssetPathHelper
    {
        public string GetResourceFolderPath(string folderName, bool forWeb = false)
        {
            return Path.Combine(GetResourcePath(), folderName);
        }

        public string GetResourcePath(bool forWeb = false)
        {
            return NSBundle.MainBundle.BundlePath;
        }

        public string GetResourceFilePath(string folder, string fileName, bool forWeb = false)
        {
            var folderPath = string.IsNullOrEmpty(folder) ? GetResourcePath() : GetResourceFolderPath(folder);

            return Path.Combine(folderPath, fileName);
        }
    }
}

Important: Make sure your files have the Build Action set to BundleResource, because otherwise you will once again get some errors flying around your head.

UWP

The implementation of the UWP Assets is once again the one with the most places involved. Let’s have a look at the implementation itself first:

using System.IO;
using XfMvvmLight.Abstractions;

namespace XfMvvmLight.UWP.PlatformImplementations
{
    public class AssetPathHelper : IAssetPathHelper
    {
        public string GetResourceFolderPath(string folderName, bool forWeb = false)
        {
            return Path.Combine(GetResourcePath(forWeb),folderName);
        }

        public string GetResourcePath(bool forWeb = false)
        {
            if (forWeb)
            {
                return $"ms-appx-web:///";
            }
            else
            {
                return $"ms-appx:///";
            }
        }

        public string GetResourceFilePath(string folder, string fileName, bool forWeb = false)
        {
            var folderPath = string.IsNullOrEmpty(folder) ? GetResourcePath(forWeb) : GetResourceFolderPath(folder, forWeb);

            return Path.Combine(folderPath,fileName);
        }
    }
}

The UWP platform uses a separate uri-scheme for all web related things. That’s where the  forWeb parameter comes in handy. If we are not loading a bundled asset for the web, we can use this implementation for other resources as well (bundled placeholder images are a good example here).

The next step is to add the assembly again to the list of assemblies that must be included, like we have done before in the OnLaunched method within App.xaml.cs:

//modified for .NET Compile
//see https://developer.xamarin.com/guides/xamarin-forms/platform-features/windows/installation/universal/#Target_Invocation_Exception_when_using_Compile_with_.NET_Native_tool_chain
List<Assembly> assembliesToInclude =
    new List<Assembly>
    {
        typeof(OsVersionService).GetTypeInfo().Assembly,
        typeof(PlatformDialogService).GetTypeInfo().Assembly, 
        typeof(AssetPathHelper).GetTypeInfo().Assembly
    };

The last step involved in the UWP project is to register the implementation with the DependencyService
after the Xamarin.Forms framework is initialized:

Xamarin.Forms.DependencyService.Register<AssetPathHelper>();

The resources should be packed with a Build Action of Content for the UWP platform.

Back to the Xamarin.Forms project

Now that we have everything in place on our platform projects as well as our Xamarin.Forms project, we finally can start using these methods. Here is an example of loading a CSS-file into a string. We can pass this string together with an HTML-string into a HtmlWebViewSource:

private static string GetCssString(string cssFileName)
{
    var resourcePath = SimpleIoc.Default.GetInstance<IAssetPathHelper>().GetResourceFolderPath("HtmlResources", true);

    return $"<link rel=\"stylesheet\" href=\"{resourcePath}/{cssFileName}\">";
}

Summary

Using the DependencyService of Xamarin.Forms allows us once again to use platform specific functionality very easily. When we are working with WebView and HTML, this comes in handy. If you have other valid scenarios for this implementations or even ideas on how to improve them, feel free to leave a comment below or ping me on my social network accounts. Otherwise, I hope this post is helpful for some of you.

As this is the last post before xmas, I wish you all a merry xmas in addition to my traditional

Happy coding, everyone!

Posted by msicc in Android, Dev Stories, iOS, Windows, Xamarin, 1 comment
Xamarin Forms, the MVVMLight Toolkit and I: showing dialog messages

Xamarin Forms, the MVVMLight Toolkit and I: showing dialog messages

In this post, I will show you how to display dialog messages (also known as message box). This time, we will use again native implementations (like in the second post about Dependency Injection) to get the job done. I could have used the Xamarin.Forms Page.DisplayAlertmethod, but that one does not allow a lot of customization, so I went down to implement it my way.

Like always, the interface dictates functionality

Because of the Xamarin Forms code being a portable class library, we need an new interface that can be called from all three platforms. I am covering four scenarios which I use frequently in my apps:

public interface IDialogService
{
    void CloseAllDialogs();

    Task ShowMessageAsync(string title, string message);

    Task ShowErrorAsync(string title, Exception error, string buttonText, Action<bool> closeAction, bool cancelableOnTouchOutside = false, bool cancelable = false);

    Task ShowMessageAsync(string title, string message, string buttonText, Action<bool> closeAction, bool cancelableOnTouchOutside = false, bool cancelable = false);

    Task ShowMessageAsync(string title, string message, string buttonConfirmText, string buttonCancelText, Action<bool> closeAction, bool cancelableOnTouchOutside = false, bool cancelable = false);
}

Of course, I want to display those message dialogs asynchronously, that’s why I wrap them in a Task. Let’s have a look into the implementation:

Android

List<AlertDialog> _openDialogs = new List<AlertDialog>();

public void CloseAllDialogs()
{
    foreach (var dialog in _openDialogs)
    {
        dialog.Dismiss();
    }
    _openDialogs.Clear();
}


public async Task ShowMessageAsync(string title, string message)
{
    await Task.Run(() => ShowAlert(title, message, "OK", null, null, false, false));
}


public async Task ShowErrorAsync(string title, Exception error, string buttonText, Action<bool> callback, bool cancelableOnTouchOutside = false, bool cancelable = false)
{
    await Task.Run(() => ShowAlert(title, error.ToString(), buttonText, null, callback, cancelableOnTouchOutside, cancelable));
}

public async Task ShowMessageAsync(string title, string message, string buttonText, Action<bool> callback, bool cancelableOnTouchOutside = false, bool cancelable = false)
{
    await Task.Run(() => ShowAlert(title, message, buttonText, null, callback, cancelableOnTouchOutside, cancelable));
}


public async Task ShowMessageAsync(string title, string message, string buttonConfirmText, string buttonCancelText, Action<bool> callback, bool cancelableOnTouchOutside = false, bool cancelable = false)
{
    await Task.Run(() => ShowAlert(title, message, buttonConfirmText, buttonCancelText, callback, cancelableOnTouchOutside, cancelable));
}


I am tunneling the defined Tasks of the interface via a call to Task.Run()  into one single method call that is able to handle all scenarios I want to support. Let’s have a look at the ShowAlert method:

internal void ShowAlert(string title, string content, string confirmButtonText = null, string cancelButtonText = null, Action<bool> callback = null, bool cancelableOnTouchOutside = false, bool cancelable = false)
{
    var alert = new AlertDialog.Builder(Forms.Context);
    alert.SetTitle(title);
    alert.SetMessage(content);

    if (!string.IsNullOrEmpty(confirmButtonText))
    {
        alert.SetPositiveButton(confirmButtonText, (sender, e) =>
        {
            callback?.Invoke(true);
            _openDialogs.Remove((AlertDialog)sender);
        });
    }

    if (!string.IsNullOrEmpty(cancelButtonText))
    {
        alert.SetNegativeButton(cancelButtonText, (sender, e) =>
        {
            callback?.Invoke(false);
            _openDialogs.Remove((AlertDialog)sender);
        });
    }

    Device.BeginInvokeOnMainThread(() =>
    {
        var dialog = alert.Show();
        _openDialogs.Add(dialog);
        dialog.SetCanceledOnTouchOutside(cancelableOnTouchOutside);
        dialog.SetCancelable(cancelable);

        if (cancelableOnTouchOutside || cancelable)
        {
            dialog.CancelEvent += (sender, e) =>
            {
                callback?.Invoke(false);
                _openDialogs.Remove((AlertDialog)sender);
            };
        }


    });
}

In the first three lines I am setting up a new AlertDialog , taking into account the actual Xamarin.Forms.Context, setting the title and the message content. If no other option is used, this shows just with the standard “OK”-Button to close the message.

Often we want to modify the button text, that’s where the confirmButtonText and cancelButtonTextoverloads are being used. I am also using a callback method that takes a Boolean to show which button on the message was pressed. Showing the Dialog needs to be done on the main UI Thread. Xamarin Forms provides the Device.BeginInvokeOnMainThread method to dispatch the code within the action into the right place.

I am showing the dialog while keeping a reference in the _openDialogs List. This reference is removed once the matching button or cancel action is executed. If the message is allowed to be dismissed via outside touches or other cancel methods, this happens in the CancelEvent Eventhandler delegate I am attaching.

The implementation also has a way to close all open dialogs, but it is a good practice to have only one open at a time, especially as other platforms do support only one open dialog at a time.

UWP

Microsoft recommends to use the ContentDialog class to show messages and dialogs of all kind. So this is what we will use to show our dialog messages. Like on Android, we need a Task.Run()wrapper to make the implementation async.

List<ContentDialog> _openDialogs = new List<ContentDialog>(); 
  
  
public void CloseAllDialogs() 
{ 
    foreach (var dialog in _openDialogs) 
    { 
        dialog.Hide(); 
    } 
    _openDialogs.Clear(); 
} 
  
  
public async Task ShowErrorAsync(string title, Exception error, string buttonText, Action<bool> closeAction, bool cancelableOnTouchOutside = false, bool cancelable = false) 
{ 
    await Task.Run(() => { ShowContentDialog(title, error.ToString(), buttonText, null, closeAction, cancelableOnTouchOutside, cancelable); }); 
} 
  
public async Task ShowMessageAsync(string title, string message) 
{ 
    await Task.Run(() => { ShowContentDialog(title, message, "OK", null, null, false, false); }); 
} 
  
public async Task ShowMessageAsync(string title, string message, string buttonText, Action<bool> closeAction, bool cancelableOnTouchOutside = false, bool cancelable = false) 
{ 
    await Task.Run(() => { ShowContentDialog(title, message, buttonText, null, closeAction, cancelableOnTouchOutside, cancelable); }); 
} 
  
public async Task ShowMessageAsync(string title, string message, string buttonConfirmText, string buttonCancelText, Action<bool> closeAction, bool cancelableOnTouchOutside = false, bool cancelable = false) 
{ 
    await Task.Run(() => { ShowContentDialog(title, message, buttonConfirmText, buttonCancelText, closeAction, cancelableOnTouchOutside, cancelable); }); 
} 
  

Even if UWP allows only one ContentDialog to be open, we are using the list of open dialogs to be able to close “them” for compatibility reasons. The next step to implement is the ShowContentDialog Task all methods above are using:

internal void ShowContentDialog(string title, string content, string confirmButtonText = null, string cancelButtonText = null, Action<bool> callback = null, bool cancelableOnTouchOutside = false, bool cancelable = false) 
{ 
    Device.BeginInvokeOnMainThread(async () => 
    { 
        var messageDialog = new ContentDialog() 
        { 
            Title = title, 
            Content = content,                     
        }; 
  
  
    if (!string.IsNullOrEmpty(confirmButtonText)) 
    { 
  
        if (string.IsNullOrEmpty(cancelButtonText)) 
        { 
            messageDialog.CloseButtonText = confirmButtonText; 
  
            messageDialog.CloseButtonClick += (sender, e) => 
            { 
                callback?.Invoke(true); 
                _openDialogs.Remove((ContentDialog)sender); 
            }; 
        } 
        else 
        { 
            messageDialog.PrimaryButtonText = confirmButtonText; 
  
            messageDialog.PrimaryButtonClick += (sender, e) => 
            { 
                callback?.Invoke(true); 
                _openDialogs.Remove((ContentDialog)sender); 
            }; 
  
        } 
    } 
  
        if (!string.IsNullOrEmpty(cancelButtonText)) 
        { 
            messageDialog.CloseButtonText = cancelButtonText; 
  
            messageDialog.CloseButtonClick += (sender, e) => 
            { 
                callback?.Invoke(false); 
                _openDialogs.Remove((ContentDialog)sender); 
            }; 
        } 
  
  
  
        _openDialogs.Add(messageDialog); 
  
        await messageDialog.ShowAsync(); 
    }); 
} 

The setup of the dialog is kind of similar to android. First, we are creating a new dialog setting the title and the message.

The second part is a bit more complex here. Dialogs should hook into the CloseButton properties and events in all cases, at least after OS-Version 1703 (Creators Update). This way, the CloseButtonClickevent is also raised when the user presses the ESC-Button, the system back button, the close button of the dialog as well as the B-Button on the Xbox-Controller.

When we have only one button to show, our confirmButtonText is directed to the CloseButton, otherwise to the PrimaryButton. In the second case, the CloseButtonis connected with the cancelButtonText. We are using the same callback Action as on Android, where the bool parameter indicates which button was pressed.

In the UWP implementation the additional parameters cancelableOnTouchOutsideand cancelableare not used.

iOS

The base implementation on iOS is basically the same like on Android and UWP. In case of iOS, we are using the UIAlertController class, which is mandatory since iOS 8.

List<UIAlertController> _openDialogs = new List<UIAlertController>();

public void CloseAllDialogs()
{
    foreach (var dialog in _openDialogs)
    {
        Device.BeginInvokeOnMainThread(() =>
        {
            dialog.DismissViewController(true, null);
        });
    }
    _openDialogs.Clear();
}

public async Task ShowErrorAsync(string title, Exception error, string buttonText, Action<bool> closeAction, bool cancelableOnTouchOutside = false, bool cancelable = false)
{
    await Task.Run(() => { ShowAlert(title, error.ToString(), buttonText, null, closeAction, cancelableOnTouchOutside, cancelable); });
}

public async Task ShowMessageAsync(string title, string message)
{
    await Task.Run(() => { ShowAlert(title, message, "OK", null, null, false, false); });
}

public async Task ShowMessageAsync(string title, string message, string buttonText, Action<bool> closeAction, bool cancelableOnTouchOutside = false, bool cancelable = false)
{
    await Task.Run(() => { ShowAlert(title, message, buttonText, null, closeAction, cancelableOnTouchOutside, cancelable); });
}

public async Task ShowMessageAsync(string title, string message, string buttonConfirmText, string buttonCancelText, Action<bool> closeAction, bool cancelableOnTouchOutside = false, bool cancelable = false)
{
    await Task.Run(() => { ShowAlert(title, message, buttonConfirmText, buttonCancelText, closeAction, cancelableOnTouchOutside, cancelable); });
}

Now that we have the Task wrappers in place, we need to implement the ShowAlert method:

internal void ShowAlert(string title, string content, string confirmButtonText = null, string cancelButtonText = null, Action<bool> callback = null, bool cancelableOnTouchOutside = false, bool cancelable = false)
{
    //all this code needs to be in here because UIKit demands the main UI Thread
    Device.BeginInvokeOnMainThread(() =>
    {
        var dialogAlert = UIAlertController.Create(title, content, UIAlertControllerStyle.Alert);

        var okAction = UIAlertAction.Create(!string.IsNullOrEmpty(confirmButtonText) ? confirmButtonText : "OK", UIAlertActionStyle.Default, _ =>
        {
            callback?.Invoke(true);
            _openDialogs.Remove(dialogAlert);
        });
        dialogAlert.AddAction(okAction);


        if (!string.IsNullOrEmpty(cancelButtonText))
        {
            var cancelAction = UIAlertAction.Create(cancelButtonText, UIAlertActionStyle.Cancel, _ =>
            {
                callback?.Invoke(false);
                _openDialogs.Remove(dialogAlert);
            });
            dialogAlert.AddAction(cancelAction);
        }
        _openDialogs.Add(dialogAlert);

        var rootController = UIApplication.SharedApplication.KeyWindow.RootViewController;

        rootController.PresentViewController(dialogAlert, true, null);
    });
}

What I am doing here is straight forward – I am setting up a new UIAlertController instance via its creation method, telling it to be styled as an alert. Then I need to create two UIAlertAction instances, one for the confirmButtonText and one for the cancelButtonText.  Of course, I am hooking up the prior defined callback action, which will inform the Xamarin.Forms class about the result of the dialog.

To display the Alert, we need a reference to the RootViewController of the iOS application. In most Xamarin.Forms applications, the above code will do the job to present the UIAlertController via the PresentViewController method provided by the OS. Like the UWP implementation, also the iOS implementation needs to be executed in the main UI thread, because the UIKit demands it. That’s why also here, the whole code is running inside the Device.BeginInvokeOnMainThread method’s action delegate.

The additional parameters cancelableOnTouchOutsideand cancelableare not used on iOS.

Updating our ViewModelLocator

If you have read my post on Dependency Injection, you might remember that we are able to combine the power of MVVMLight with Xamarin.Forms own DependencyService. This is what we will do once again in our ViewModelLocator in the RegisterServices method:

var dialogService = DependencyService.Get<IDialogService>();
SimpleIoc.Default.Register<IDialogService>(() => dialogService);

And that’s already all we need to do here. Really.

Finally: Showing message dialogs

I added three buttons to the sample’s main page. One shows a simple message, one shows the exception that I manually throw and the last one provides a two choice dialog. Let’s have a quick look at the commands bound to them:

 
private RelayCommand _showMessageCommand; 
  
public RelayCommand ShowMessageCommand => _showMessageCommand ?? (_showMessageCommand = new RelayCommand(async () => 
{ 
    await SimpleIoc.Default.GetInstance<IDialogService>().ShowMessageAsync("Cool... ?", "You really clicked this button!"); 
}));

This shows a simple message without using the additional parameters. I use this one primarily for confirmations.

private RelayCommand _showErrorWithExceptionCommand;

public RelayCommand ShowErrorWithExceptionCommand => _showErrorWithExceptionCommand ?? (_showErrorWithExceptionCommand = new RelayCommand(async () =>
{
    try
    {
        throw new NotSupportedException("You tried to fool me, which is not supported!");
    }
    catch (Exception ex)
    {
        await SimpleIoc.Default.GetInstance<IDialogService>().ShowErrorAsync("Error", ex, "Sorry",
            returnValue =>
            {
                Debug.WriteLine($"{nameof(ShowErrorWithExceptionCommand)}'s dialog returns: {returnValue}");
            }, false, false);
    }
}));

This one takes an exception and shows it on the screen. I use them mainly for developing purposes.

private RelayCommand _showSelectionCommand;

public RelayCommand ShowSelectionCommand => _showSelectionCommand ?? (_showSelectionCommand = new RelayCommand(async () =>
{

    await SimpleIoc.Default.GetInstance<IDialogService>().ShowMessageAsync("Question:",
        "Do you enjoy this blog series about MVVMLight and Xamarin Forms?", "yeah!", "nope", async returnvalue =>
        {
            if (returnvalue)
            {
                await SimpleIoc.Default.GetInstance<IDialogService>()
                    .ShowMessageAsync("Awesome!", "I am glad you like it");
            }
            else
            {
                await SimpleIoc.Default.GetInstance<IDialogService>()
                    .ShowMessageAsync("Oh no...", "Maybe you could send me some feedback on how to improve it?");
            }
        },
        false, false);

}));

This one provides a choice between two options. A good example where I use this is when there is no internet connection and I ask the user to open the WiFi settings or cancel. In the sample, I am also showing another simple message after one of the buttons has been pressed. The content of this simple message depends on which button was clicked.

Action, please!

This slideshow requires JavaScript.

 

Using Xamarin.Forms’ DependencyServicetogether with the SimpleIoc implementation of MVVMLight, we are once again easily able to connect platform specific code to our Xamarin.Forms project. Every platform implementation follows the dialog recommendations and is executed using the native implementations while keeping some options open to use different kind of message dialogs.

As always, I hope this post is helpful for some of you. Until the next post, happy coding!

Posted by msicc in Android, Dev Stories, iOS, Windows, Xamarin, 1 comment
[Updated] Xamarin Forms, the MVVMLight Toolkit and I: navigation and modal pages

[Updated] Xamarin Forms, the MVVMLight Toolkit and I: navigation and modal pages

After showing you the basic MVVMLight Setup I am using as well as how to combine Xamarin.Forms’ DependencyService with our own Dependecy Injection-mechanism, this article is all about Navigation and modal pages in Xamarin.Forms.

Some preliminary words

I always liked how the MVVMLight Toolkit provided the NavigationService for my Windows & Windows Phone applications. That said, I tried to keep the idea of its NavigationService and ported it over to my Xamarin.Forms structure. I also extended it to fit the needs of my XF apps, for example for pushing modal pages (we’ll see that later). So for the base idea, I have to say thanks to Laurent Bugnion for the Windows platform implementation in MVVMLight and for keeping it simple. The later fact allows one to easily extend and adapt the service.

Also, there are tons of really good and also working samples around the web to show navigation in Xamarin.Forms. However, most of them follow another strategy, so I came to the point where decided to write my own implementation. My way may work for most scenarios without making a difference between navigation and showing modal pages. It provides an easy implementation and usage while following the MVVM pattern as far as it can go with Xamarin.Forms.

Components

My implementation has some key components, which I will describe briefly in this post:

  • Interfaces for Navigation and view events
  • Implementation of these interfaces
  • using the implementations in views
  • using the implementations in ViewModels

Interfaces for the win!

If we want to follow the MVVM pattern, we need to keep our Views completely separated from our ViewModels. A common practice to achieve this goal is the usage of interfaces, and so do I. If you have read my previous blog post about Dependency Injection, you know already a bit about the things we have to do. First, we will create an interface for the navigation itself:

public interface IXfNavigationService
{
    void Initialize(NavigationPage navigation);
    void Configure(string pageKey, Type pageType);
    (bool isRegistered, bool isModal) StackContainsNavKey(string pageKey);
    Task GoHomeAsync();
    Task GoBackAsync();
    Task GoBackModalAsync();
    int ModalStackCount { get; }
    string CurrentModalPageKey { get; }
    Task ShowModalPageAsync(string pageKey, bool animated = true);
    Task ShowModalPageAsync(string pageKey, object parameter, bool animated = true);
    int NavigationStackCount { get; }
    string CurrentPageKey { get; }
    Task NavigateToAsync(string pageKey, bool animated = true);
    Task NavigateToAsync(string pageKey, object parameter, bool animated = true);
}

As you can see, it is a pretty big interface that covers nearly all possible navigation scenarios of a Xamarin.Forms app (at least those that I had to deal with already). We are going to hook into a Xamarin.Forms  NavigationPage and configure our page handling cache with the configure method. As all navigation methods in Xamarin Forms are async, all navigational tasks will be implemented async as well. Additionally, we will have some helpful properties that we can use in our application implementation.

In Windows applications, we have some additional events for pages like  Loaded or OnNavigatedTo(and so on). Xamarin.Forms pages have something similar: ViewAppearing and ViewDisappearing. These two events correspond to OnNavigatedTo and OnNavigatedFrom in a Windows application. Sometimes, we need to know when those events happen in our ViewModels or other places behind the scenes to get stuff done. That’s why I created an interface for this purpose:

public interface IViewEventBrokerService
{
    event EventHandler<ViewEventBrokerEventArgs> ViewAppearing;
    event EventHandler<ViewEventBrokerEventArgs> ViewDisAppearing;
    void RaiseViewAppearing(string pageKey, Type pageType, bool isModal);
    void RaiseViewDisAppearing(string pageKey, Type pageType, bool isModal);
}

I also created my own EventArgs to make it easier to pass all data around (as you can see above). Here is the class:

public class ViewEventBrokerEventArgs : EventArgs
{
    public ViewEventBrokerEventArgs(string pageKey, Type pageType, bool isModal)
    {
        PageKey = pageKey;
        PageType = pageType;
        IsModal = isModal;
    }
    public string PageKey { get; private set; }
    public Type PageType { get; private set; }
    public bool IsModal { get; private set; }
}

Now that we have our interfaces defined, we are going the next step and have a look into their implementations.

IXfNavigationService implementation

First it is important to understand what Xamarin.Forms does when we are using a NavigationPage. We are performing a hierarchical navigation here. A must read is this page from the Xamarin documentation.

So you’re back from reading the documentation? Great, then lets have a look what our XfNavigationServiceimplementation does. It builds on top of the Xamarin.Forms NavigationPage, which implements the INavigation interface. Other solutions I saw are pulling down this interface and implement it in their implementation. I prefer to keep it simple and just use the already implemented interface in the Xamarin.Forms NavigationPage, which is mandatory for my implementation. We are pulling it into our implementation with the Initialize(NavigationPage navigationPage) method:

public void Initialize(NavigationPage navigationPage)
{
    _navigationPage = navigationPage;
}

We could also use a constructor injection here. Most of the time it would work, but I had some problems to get the timing right under certain circumstances. That’s why I prefer to manually initialize it to overcome these timing problems  – and it does not really hurt in real life applications to have it a bit more under control.

Before we are going to see the actual implementation, I need to break out of this scope and explain a threading issue.

Locking the current executing thread

Often we are running a lot of code at the same time. This could bring up some issues with ourXfNavigationService. If multiple threads try to change or execute code from our interface implementation, the result may not the one we desired. To prevent this, we would normally use the lock statement to allow only one thread modifying our code. Problem with the lock statement is, that we cannot run asynchronous code within.

As asynchronous code execution removes some of the headache in programming, we need a solution for this. And there is one, utilizing the SemaphoreSlim class of the .NET framework. Basically, the SemaphoreSlim class provides a single application Semaphore implementation. Without going deeper into it, we are replacing the lock statement we would normally use with this. We just need to initialize the SemaphoreSlim class as a private static member:

//using this instead of lock statement
private static SemaphoreSlim _lock = new SemaphoreSlim(1, 1);

This will only allow one thread to access the objects we are passing after it, and before releasing it (which is our responsibility). The code itself is recommended to run in a try..finally  block (you’ll see that below). Pretty much the same thing the lock statement would do, but with the possibility to run asynchronous code.

Back to the implementation

The next step is to allow registering pages with a name and their type from the ViewModelLocator. I am doing the same as in the standard MVVMLight implementation here:

//declare private member for the registered pages cache
private readonly Dictionary<string, Type> _pagesByKey = new Dictionary<string, Type>();


public void Configure(string pageKey, Type pageType) 
{ 
    //synchronous lock 
    _lock.Wait(); 
    try 
    { 
        if (_pagesByKey.ContainsKey(pageKey)) 
        { 
            _pagesByKey[pageKey] = pageType; 
        } 
        else 
        { 
            _pagesByKey.Add(pageKey, pageType); 
        } 
    } 
    finally 
    { 
        _lock.Release(); 
    } 
} 

Once we have our pages configured (we’ll see that later in this post), we are easily able to identify our registered pages for navigation purposes. Sometimes we want to get the current Page, so we need to implement a property for that, as well as for the current page key and the current modal page key:

public Page GetCurrentPage()
{
    return _navigationPage?.CurrentPage;
}


public string CurrentPageKey 
{ 
    get 
    { 
        _lock.Wait(); 
        try 
        { 
            if (_navigationPage?.CurrentPage == null) 
            { 
                return null; 
            } 
  
            var pageType = _navigationPage.CurrentPage.GetType(); 
  
            return _pagesByKey.ContainsValue(pageType) 
                ? _pagesByKey.First(p => p.Value == pageType).Key 
                : null; 
        } 
        finally 
        { 
            _lock.Release(); 
        } 
    } 
} 


public string CurrentModalPageKey 
{ 
    get 
    { 
        _lock.Wait(); 
  
        try 
        { 
            if (ModalStackCount == 1) 
            { 
                return null; 
            } 
  
            //only INavigation holds the ModalStack 
            var pageType = _navigationPage.Navigation.ModalStack.Last().GetType(); 
  
            return _pagesByKey.ContainsValue(pageType) ? _pagesByKey.FirstOrDefault(p => p.Value == pageType).Key 
                : null; 
        } 
        finally 
        { 
            _lock.Release(); 
        } 
    } 
} 

I found myself in situations where I needed to check if I am on a modal page, so lets add a method for that:

public (bool isRegistered, bool isModal) StackContainsNavKey(string pageKey) 
{ 
  
    bool isUsedModal = false; 
    bool isRegistered = false; 
  
    _lock.Wait(); 
    try 
    { 
        isRegistered = _pagesByKey.ContainsKey(pageKey); 
  
  
        if (isRegistered) 
        { 
            var pageType = _pagesByKey.SingleOrDefault(p => p.Key == pageKey).Value; 
  
            var foundInNavStack = _navigationPage.Navigation.NavigationStack.Any(p => p.GetType() == pageType); 
            var foundInModalStack = _navigationPage.Navigation.ModalStack.Any(p => p.GetType() == pageType); 
  
            if (foundInNavStack && !foundInModalStack || !foundInNavStack && !foundInModalStack) 
            { 
                isUsedModal = false; 
            } 
            else if (foundInModalStack && !foundInNavStack) 
            { 
                isUsedModal = true; 
            } 
            else 
            { 
                throw new NotSupportedException("Pages should be used exclusively Modal or for Navigation"); 
            } 
        } 
        else 
        { 
            throw new ArgumentException($"No page with key: {pageKey}. Did you forget to call the Configure method?", nameof(pageKey)); 
        } 
    } 
    finally 
    { 
        _lock.Release(); 
    } 
  
    return (isRegistered, isUsedModal); 
} 

This method checks if a page is registered correctly with its key and if it is presented modal at the moment. It also makes sure that an exception is thrown if a page is used modal AND in a navigation flow, which I think is a good practice to avoid problems when using the service. Finally, let’s add a Task for navigation to another page:

public async Task NavigateToAsync(string pageKey, object parameter, bool animated = true) 
{ 
    await _lock.WaitAsync(); 
  
    try 
    { 
  
        if (_pagesByKey.ContainsKey(pageKey)) 
        { 
            var type = _pagesByKey[pageKey]; 
            ConstructorInfo constructor = null; 
            object[] parameters = null; 
  
            if (parameter == null) 
            { 
                constructor = type.GetTypeInfo() 
                    .DeclaredConstructors 
                    .FirstOrDefault(c => !c.GetParameters().Any()); 
  
                parameters = new object[] 
                { 
                }; 
            } 
            else 
            { 
                constructor = type.GetTypeInfo() 
                    .DeclaredConstructors 
                    .FirstOrDefault( 
                        c => 
                        { 
                            return c.GetParameters().Count() == 1 
                                   && c.GetParameters()[0].ParameterType == parameter.GetType(); 
                        }); 
  
                parameters = new[] { parameter }; 
            } 
  
            if (constructor == null) 
            { 
                throw new InvalidOperationException("No constructor found for page " + pageKey); 
            } 
  
            var page = constructor.Invoke(parameters) as Page; 
            if (_navigationPage != null) 
            { 
                Device.BeginInvokeOnMainThread(async () => 
                { 
                    await _navigationPage.Navigation.PushAsync(page, animated); 
                }); 
            } 
            else 
            { 
                throw new NullReferenceException("there is no navigation page present, please check your page architecture and make sure you have called the Initialize Method before."); 
            } 
        } 
        else 
        { 
            throw new ArgumentException( 
                $"No page with key: {pageKey}. Did you forget to call the Configure method?", 
                nameof(pageKey)); 
        } 
    } 
    finally 
    { 
        _lock.Release(); 
    } 
} 

[Update:] Until the latest Xamarin.Forms release, there was no need to use the Device.BeginInvokeOnMainThreadmethod for the navigation itself. I only checked it recently that the Navigation needs now the call to it to dispatch the navigation to the main UI thread. Otherwise, the navigation will end in a dead thread on Android and iOS (but strangely not for UWP). I updated the code above and the source on Github as well.

I try to avoid passing parameters to pages while navigating, that’s why I added another task without the parameter overload as well:

public async Task NavigateToAsync(string pageKey, bool animated = true)
{
    await NavigateToAsync(pageKey, null, animated);
}

Of course we will run into situations where we want to go back manually, for example in a cancel function. So we have a method for that as well:

public async Task GoBackAsync()
{
    await _navigationPage.Navigation.PopAsync(true);
}

That’s  a lot of functionality already, but until now, we are only able to navigate to other pages and back. We want to support modal pages as well, so we’ll have some more methods to implement. They are following the same syntax as their navigation counterpart, so no surprise here:

public async Task ShowModalPageAsync(string pageKey, object parameter, bool animated = true) 
{ 
    await _lock.WaitAsync(); 
  
    try 
    { 
  
        if (_pagesByKey.ContainsKey(pageKey)) 
        { 
            var type = _pagesByKey[pageKey]; 
            ConstructorInfo constructor = null; 
            object[] parameters = null; 
  
            if (parameter == null) 
            { 
                constructor = type.GetTypeInfo() 
                    .DeclaredConstructors 
                    .FirstOrDefault(c => !c.GetParameters().Any()); 
  
                parameters = new object[] 
                { 
                }; 
            } 
            else 
            { 
                constructor = type.GetTypeInfo() 
                    .DeclaredConstructors 
                    .FirstOrDefault( 
                        c => 
                        { 
                            return c.GetParameters().Count() == 1 
                                   && c.GetParameters()[0].ParameterType == parameter.GetType(); 
                        }); 
  
                parameters = new[] { parameter }; 
            } 
  
  
            var page = constructor.Invoke(parameters) as Page; 
  
            if (_navigationPage != null) 
            { 
                Device.BeginInvokeOnMainThread(async () => 
                { 
                    await _navigationPage.Navigation.PushModalAsync(page, animated); 
                }); 
            } 
            else 
            { 
                throw new NullReferenceException("there is no navigation page present, please check your page architecture and make sure you have called the Initialize Method before."); 
            } 
        } 
        else 
        { 
            throw new ArgumentException($"No page found with key: {pageKey}. Did you forget to call the Configure method?", nameof(pageKey)); 
        } 
    } 
    finally 
    { 
        _lock.Release(); 
    } 
} 

public async Task GoBackModalAsync()
{
    await _navigationPage.Navigation.PopModalAsync(true);
}

The last things that I wanted to note are properties that return the current counts of both the modal and the navigation stack of the navigation page as well as the GoHomeAsync() method to return to the root page. You will see these in the source code on Github.

IViewEventBrokerService implementation

Like I wrote already before, sometimes we need to know when a view is appearing/disappearing. In that case, the IViewEventBrokerService comes in. The implementation is pretty straight forward:

public class ViewEventBrokerService : IViewEventBrokerService
{
    public event EventHandler<ViewEventBrokerEventArgs> ViewAppearing;
    public event EventHandler<ViewEventBrokerEventArgs> ViewDisAppearing;


    public void RaiseViewAppearing(string pageKey, Type pageType, bool isModal)
    {
        ViewAppearing?.Invoke(this, new ViewEventBrokerEventArgs(pageKey, pageType, isModal));
    }

    public void RaiseViewDisAppearing(string pageKey, Type pageType, bool isModal)
    {
        ViewDisAppearing?.Invoke(this, new ViewEventBrokerEventArgs(pageKey, pageType, isModal));
    }
}

We have two events that can be raised with their corresponding Raise… methods. That’s all we have to do in here.

Implementing the Interfaces in Views (Pages)

Well, we have written a lot of code until here, now it is time to actually use it. As I want all my pages to be raise their appearing and disappearing automatically, I created a new base class, derived from Xamarin.Forms’ ContentPage:

public class XfNavContentPage : ContentPage
{
    private IViewEventBrokerService _viewEventBroker;
    private IXfNavigationService _navService;

    public XfNavContentPage()
    {
        _viewEventBroker = SimpleIoc.Default.GetInstance<IViewEventBrokerService>();
        _navService = SimpleIoc.Default.GetInstance<IXfNavigationService>();
    }

    public static BindableProperty RegisteredPageKeyProperty = BindableProperty.Create("RegisteredPageKey", typeof(string), typeof(XfNavContentPage), default(string), BindingMode.Default, propertyChanged: OnRegisteredPageKeyChanged);

    private static void OnRegisteredPageKeyChanged(BindableObject bindable, object oldValue, object newValue)
    {
        //todo, but not needed atm.
    }

    public string RegisteredPageKey
    {
        get { return (string)GetValue(RegisteredPageKeyProperty); }
        set { SetValue(RegisteredPageKeyProperty, value); }
    }

    private(bool isRegistered, bool isModal) _stackState { get; private set; }

    protected override void OnAppearing()
    {
        base.OnAppearing();

        if (!string.IsNullOrEmpty(RegisteredPageKey))
        {
            _stackState = _navService.StackContainsNavKey(RegisteredPageKey);

            if (_stackState.isRegistered)
            {
                _viewEventBroker?.RaiseViewAppearing(RegisteredPageKey, GetType(), _stackState.isModal);
            }
        }
    }

    protected override void OnDisappearing()
    {
        base.OnDisappearing();

        if (!string.IsNullOrEmpty(RegisteredPageKey))
        {
            if (_stackState.isRegistered)
            {
                _viewEventBroker?.RaiseViewDisAppearing(RegisteredPageKey, GetType(), _stackState.isModal);
            }
        }
    }
}

Let me explain what this base class does. It provides a BindableProperty(which is the Xamarin.Forms version of a DependencyProperty) that takes the registered key to provide it as a parameter. You can bind the key from your ViewModel or set it in code behind/XAML directly, all these ways will work. The base page implements our interfaces of the IXfNavigationService and the IViewBrokerServiceinterfaces as members and loads them in its constructor. The _stackStatemember is just an easy way to provide the needed data to the event-raising methods of our IViewEventBrokerservice. If used correctly, every page that derives from this base class will throw the corresponding events, which can be used in other parts of our application then.

Implementing the Interfaces in ViewModels

Like I did for Views, I am also creating a new base class for ViewModels that implement our two interfaces. Here is how it looks like:

public class XfNavViewModelBase : ViewModelBase
{
    protected readonly IXfNavigationService _navService;

    protected readonly IViewEventBrokerService _viewEventBroker;


    public XfNavViewModelBase()
    {
        _navService = SimpleIoc.Default.GetInstance<IXfNavigationService>();
        _viewEventBroker = SimpleIoc.Default.GetInstance<IViewEventBrokerService>();
        _viewEventBroker.ViewAppearing += OnCorrespondingViewAppearing;
        _viewEventBroker.ViewDisAppearing += OnCorrespondingViewDisappearing;
    }


    protected virtual void OnCorrespondingViewAppearing(object sender, ViewEventBrokerEventArgs e)
    {
    }

    protected virtual void OnCorrespondingViewDisappearing(object sender, ViewEventBrokerEventArgs e)
    {
    }

    private string _correspondingViewKey;

    public string CorrespondingViewKey
    {
        get => _correspondingViewKey; set => Set(ref _correspondingViewKey, value);
    }

    public bool IsBoundToModalView()
    {
        if (!string.IsNullOrEmpty(CorrespondingViewKey))
        {
            var checkIfIsModal = _navService.StackContainsNavKey(CorrespondingViewKey);

            if (checkIfIsModal.isRegistered)
            {
                return checkIfIsModal.isModal;
            }
        }
        return false;
    }
}

The XfNavViewModelBase implements both the IXfNavigationService and the IViewEventBrokerService interfaces. It also registers for the events thrown by IViewEventBrokerService. Making them virtual allows us to override them in derived ViewModels, if we need to. It has a string property to set the name of the View we want to use it in as well as a method that checks automatically if we are on a modal page if it gets called.

Using the created navigation setup

Now our navigation setup is ready it is time to have a look into how to use it. First we are going to create two pages, one for being shown modal, and one for being navigated to.

created-pages-snip

Then we’ll need to register and configure our services and pages in the ViewModelLocator. Remember the static RegisterServices() method we created in the first post? This is were we will throw the interface registrations in:

private static void RegisterServices() 
{ 
    //this one gets the correct service implementation from platform implementation 
    var osService = DependencyService.Get<IOsVersionService>(); 
  
    // which can be used to register the service class with MVVMLight's Ioc 
    SimpleIoc.Default.Register<IOsVersionService>(() => osService); 
  
    SimpleIoc.Default.Register<IXfNavigationService>(GetPageInstances); 
    SimpleIoc.Default.Register<IViewEventBrokerService, ViewEventBrokerService>(); 
}

The method contains already our IOsVersionService from my last blog post. We are also using DI here for the IXfNavigationService, as we need to Configure our page keys as well. First, we are adding static strings as page keys to the ViewModelLocator:

public static string ModalPageKey => nameof(ModalPage);
public static string NavigatedPageKey => nameof(NavigatedPage);

I am always using the nameof()-operator for this, because it makes life a bit easier. The second step is the missing piece in the service registration, the GetPageInstances() method that returns an instance of the IXfNavigationService interface implentation and registers our two pages via the Configure() method.

public static XfNavigationService GetPageInstances()
{
    var nav = new XfNavigationService();

    nav.Configure(ModalPageKey, typeof(ModalPage));
    nav.Configure(NavigatedPageKey, typeof(NavigatedPage));

    return nav;
}

This all will never work if we are not using a NavigationPage. So we are going to load MainPage.xaml as a NavigationPagein App.xaml.cs and pass the navigation page to our IXfNavigationService:

public App()
{
    InitializeComponent();

    var rootNavigation = new NavigationPage(new View.MainPage());
    MainPage = rootNavigation;
    SimpleIoc.Default.GetInstance<IXfNavigationService>().Initialize(rootNavigation);
}

Modifying pages

Until now, the pages we created are standard ContentPages, so we need to make them derive from our earlier created base class. First we are adding the namespace of our base class to it:

xmlns:baseCtrl="clr-namespace:XfMvvmLight.BaseControls;assembly=XfMvvmLight"

After that, we are able to use the base class to change ContentPage to baseCtrl:XfNavContentPage. Now open the code behind file for the page and change it to derive from XfNavContentPage as well:

public partial class ModalPage : XfNavContentPage

The sample project has a modal page and a navigated page to demonstrate both ways.

Setting up ViewModels

Normally, we also have a ViewModel for every additional page. That´s why I created a ModalPageViewModel as well as a NavigatedPageViewModel for the sample. They derive from the XfNavViewModelBase, so wie can easyily hook up the view events (if needed):

public class ModalPageViewModel : XfNavViewModelBase
{ 
    public ModalPageViewModel()
    {
        CorrespondingViewKey = ViewModelLocator.ModalPageKey;
    }
 
    protected override void OnCorrespondingViewAppearing(object sender, ViewEventBrokerEventArgs e)
    {
        base.OnCorrespondingViewAppearing(sender, e);
    }
 
    protected override void OnCorrespondingViewDisappearing(object sender, ViewEventBrokerEventArgs e)
    {
        base.OnCorrespondingViewDisappearing(sender, e);
    }
 
 
    private RelayCommand _goBackCommand;
 
    public RelayCommand GoBackCommand => _goBackCommand ?? (_goBackCommand = new RelayCommand(async () =>
    {
        await _navService.GoBackModalAsync();
    })); 
}

Remember the page keys we added earlier to the ViewModelLocator? We are just setting it as CorrespondingViewKey in the constructor here, but you could choose to it the way you prefer as well and can easily be bound to the view now:

<baseCtrl:XfNavContentPage 
   xmlns="http://xamarin.com/schemas/2014/forms"
   xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
   xmlns:baseCtrl="clr-namespace:XfMvvmLight.BaseControls;assembly=XfMvvmLight"
   x:Class="XfMvvmLight.View.ModalPage" 
   RegisteredPageKey="{Binding CorrespondingViewKey}">
<!--code omitted for readability-->
</baseCtrl:XfNavContentPage>

As the sample pages have a button with back function, we have also a GoBackCommand bound to it. Now that’s already all we need to, and if we set a breakpoint in the event handling overrides, it will be hit as soon as the view is about to appear. See the sample working here in action on Android:

navigation-service-xf-mvvmlight

No we have a fully working and MVVM compliant navigation solution in Xamarin.Forms using the MVVMLight Toolkit.  I know there are several other toolkits and helpers floating around, but I like it to have less dependencies.  Another advantage of going this route: I am mostly familiar with the setup as I am already used to it from my Windows and Windows Phone applications. Creating this setup for navigation does not take a whole lot of time (the initial setup took me around 3 hours including conception). One can also pack the interfaces and the implementations in a (portable) class library to have all this work only once.

If you have feedback for me, feel free to leave a comment below. Like always, I hope this article is helpful for some of you. The updated sample application can be found here on my Github account.

Happy coding, everyone!

 

Posted by msicc in Android, Dev Stories, iOS, Windows, Xamarin, 4 comments
Xamarin Forms, the MVVMLight Toolkit and I: Dependecy Injection

Xamarin Forms, the MVVMLight Toolkit and I: Dependecy Injection

Recap

Let’s just do a small recap what Dependency Injection means. The DI pattern’s main goal is to decouple objects and their dependencies. To separate concerns, we are using this structure nearly every time:

  • interface which defines the provided functionality
  • service class which provides the functionality defined in the interface
  • container that allows client classes/objects to use the functionality defined in the interface

The interface, our helpful dictator

DI always involves an interface, which dictates the functionality of the implementation. In Xamarin Forms, the interface rests inside the PCL/common project:

public interface IOsVersionService
{
    string GetOsVersion { get; } 
}

This interface gets the current installed version of the operating system. The next step ist to create the platform implementation, which is commonly defined as a service class.

Platform Implementation (service class)

We need to implement the service class for each platform. The setup is pretty easy, just add a new class and implement the interface for each platform:

implement-interface-vs2017

Tip: I am using a separate folder for platform implementations and set it to be a namespace provider. This makes it easier to maintain and I keep the same structure in all platform projects.

Let’s have a look into the specific implementations:

Android

public string GetOsVersion
{
    get
    {
        var versionNb = Build.VERSION.Release;
        var codename = Build.VERSION.Codename;
 
        return $"Android {versionNb} ({codename})";
    }
}

iOS

public string GetOsVersion
{
    get
    {
        try
        {
            return $"iOS {UIDevice.CurrentDevice.SystemVersion} ({UIDevice.CurrentDevice.UserInterfaceIdiom})";
 
        }
        catch
        {
            return "This demo supports iOS only for the moment";
        }
    }
}

Windows

public string GetOsVersion
{
    get
    {
        var currentOS = AnalyticsInfo.VersionInfo.DeviceFamily;
 
        var v = ulong.Parse(AnalyticsInfo.VersionInfo.DeviceFamilyVersion);
        var v1 = (v & 0xFFFF000000000000L) >> 48;
        var v2 = (v & 0x0000FFFF00000000L) >> 32;
        var v3 = (v & 0x00000000FFFF0000L) >> 16;
        var v4 = v & 0x000000000000FFFFL;
        var versionNb = $"{v1}.{v2}.{v3}.{v4}";
 
        return $"{currentOS} {versionNb} ({AnalyticsInfo.DeviceForm})";
    }
}

Now that we are able to fetch the OS Version, we need to make the implemation visible outside of the platform assemblies. On Android and iOS, this one is pretty straigt forward by adding this Attribute on top of the class:

[assembly: Xamarin.Forms.Dependency(typeof(OsVersionService))]

Because Universal Windows projects compile differently, we need to go a different route on Windows. To make the implementation visible, we need to explicit declare the class as an assembly to remain included first (otherwise the .NET Toolchain is likely to strip it away):

protected override void OnLaunched(LaunchActivatedEventArgs e)
{
    //other code for initialization, removed for readabilty
 
    //modified for .NET Compile
    List<Assembly> assembliesToInclude = new List<Assembly>();
    assembliesToInclude.Add(typeof(OsVersionService).GetTypeInfo().Assembly);

    Xamarin.Forms.Forms.Init(e, assembliesToInclude); 
}

Now that we have our platform implementations in place, we can go ahead and use the interface to get the OS versions.

Xamarin Forms DependencyService

With the static DependencyService class, Xamarin Forms provides a static container that is able to resolve interfaces to their native platform implementations. Using it is, once again, pretty straight forward:

private string _osVersionViaDs;
public string OsVersionViaDs
{
    get { return _osVersionViaDs; }
    set { Set(ref _osVersionViaDs, value); }
}
 
private RelayCommand _getOSVersionViaDsCommand;
 
public RelayCommand GetOsVersionViaDsCommand => _getOSVersionViaDsCommand ?? (_getOSVersionViaDsCommand = new RelayCommand(() =>
{
    OsVersionViaDs = DependencyService.Get().GetOsVersion; 
}));

In my sample application, I am using a button that fetches the OS version via Xamarin Forms DependencyService and display it into a label in my view.

Special case UWP, once again

To make this acutally work in an UWP application, we need to register the Service manually. Xamarin recommends to do so in the OnLaunched event, after Xamarin Forms is initialized:

//in OnLaunched event (App.xaml.cs)
//manually register for DependencyService 
//AFTER Forms is initialized but BEFORE VMLocator is initialized:
Xamarin.Forms.DependencyService.Register<OsVersionService>();

Only with that extra line of code, it will actually work like it should. If you want to know more on the fact that UWP needs a separate solution, take a look here into the Xamarin docs.

Why use the MVVMLight Toolkit’s Ioc?

There are several reasons. First is of course, purely personal: because I am used to write code for it. But there are also technical reasons:

  • support for cunstroctor injection
  • interface implementations can have parametered constructors
  • MVVMLight supports additional features like named instances and immediate creation on registration
  • in MVVM(Light) applications, you are very likely using DI on Xamarin Forms level, anyway (like in a NavigationService)

You see, there are some (in my opinion) good reasons to use the built in Ioc of the MVVMLight Toolkit.

How to use SimpleIoc and DependencyService together

If you are not relying on the DI-System of Xamarin Forms, you will have to write a lot of code yourself to get the platform implementations. That is not necessary, though. As our ViewModelLocator normally is already part of the Xamarin Forms PCL project, it has access  to the DependencyService and can be used to get the right implementation:

//this one gets the correct service implementation from platform implementation
var osService = DependencyService.Get();
 
// which can be used to register the service class with MVVMLight's Ioc
SimpleIoc.Default.Register<IOsVersionService>(() => osService);

This works pretty well for most implementations and provides us the possibility to use all the features MVVMLight provides. The usage then matches to what we are already familiar with:

private string _osVersionViaSimpleIoc;
public string OsVersionViaSimpleIoc
{
    get { return _osVersionViaSimpleIoc; }
    set { Set(ref _osVersionViaSimpleIoc, value); }
} 
 
private RelayCommand _getOSVersionViaSimpleIocCommand;
 
public RelayCommand GetOsVersionViaSimpleIocCommand => _getOSVersionViaSimpleIocCommand ?? (_getOSVersionViaSimpleIocCommand = new RelayCommand(() =>
{
    OsVersionViaSimpleIoc = SimpleIoc.Default.GetInstance().GetOsVersion; 
}));

If you do not want (or it is not possible due to complexity) register the platform implementation directly in the ViewModelLocator, you  can go down another route. You could create a new interface in the Xamarin Forms PCL which references the interface with the platform implentation as a member. Your implementation of this new interface (in Xamarin Forms) will be responsible for the getting the instance of the platform implementation via the built in DepenencyService.

I already used both ways in my recent Xamarin projects, but I prefer the first way that I described above.

Conclusion

Due to the fact that we know the DI pattern already as we (of course) follow the MVVM pattern in our applications, there is no big mystery about using the built in DependencyService of Xamarin Forms. We can easily integrate it into the MVVMLight Toolkit and combine the best of both worlds this way.

Nonetheless, I know that also some beginners are following my posts, so I tried to describe everything a bit more extended. As always, I hope this post is helpful for some of you. In my next post, I will show you how I solved the Navigation “problem” in my Xamarin Forms applications. In the meantime, you can already have a look at the sample code on Github.

Happy coding, everyone!

Posted by msicc in Android, Dev Stories, iOS, Windows, Xamarin, 6 comments