mvvm

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
Some helpful extensions when dealing with types in .NET

Some helpful extensions when dealing with types in .NET

If you are writing reusable code, chances are high that you will write quite some code that deals with types, generics, and interfaces. Over the years, the collection of my helper extensions for that have grown. As some of my upcoming posts use them, I share them (also) for future reference.

1. Check if a Type is deriving from another Type

Deriving types is a common practice. To some extent, you can use pattern matching. Sometimes, that isn’t enough, though (especially if you have a multi-level derivation path). This is when I use one of these two extensions:

        public static bool IsDerivingFrom(this Type type, Type searchType)
        {
            if (type == null) throw new NullReferenceException();

            return
                type.BaseType != null &&
                (type.BaseType == searchType ||
                type.BaseType.IsDerivingFrom(searchType));
        }

        public static bool IsDerivingFromGenericType(this Type type, Type searchGenericType)
        {
            if (type == null) throw new ArgumentNullException(nameof(type));
            if (searchGenericType == null) throw new ArgumentNullException(nameof(searchGenericType));

            return
                type != typeof(object) &&
                (type.IsGenericType &&
                searchGenericType.GetGenericTypeDefinition() == searchGenericType ||
                IsDerivingFromGenericType(type.BaseType, searchGenericType));
        }

Update 1: Type.IsSubclassOf will give you the same result as IsDerivingFrom. The main purpose was (is) to use my implementation when having multiple levels of derivation and being able to debug the whole detection process.

2. Get type of T from IEnumerable<T>

Sometimes, one needs to know the item type of an IEnumerable<T>. These two extensions will help you in this case:

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "Extension method")]
        public static Type GetItemType<T>(this IEnumerable<T> enumerable) => typeof(T);

        public static Type? GetItemType(this object enumerable)
            => enumerable == null ? null :
            (enumerable.GetType().GetInterface(typeof(IEnumerable<>).Name)?.GetGenericArguments()[0]);

3. Check if a type implements a certain interface

Interfaces are supposed to make the life of a developer easier. Like with the type derivation, sometimes we need to know if a type implements a certain interface. This extension answers the question for you:

        public static bool ImplementsInterface(this Type? type, Type? @interface)
        {
            bool result = false;

            if (type == null || @interface == null)
                return result;

            var interfaces = type.GetInterfaces();
            if (@interface.IsGenericTypeDefinition)
            {
                foreach (var item in interfaces)
                {
                    if (item.IsConstructedGenericType && item.GetGenericTypeDefinition() == @interface)
                        result = true;
                }
            }
            else
            {
                foreach (var item in interfaces)
                {
                    if (item == @interface)
                        result = true;
                }
            }

            return result;
        }

Update 2: Type.IsAssignableFrom will also tell you if a type implements an interface. As for the IsDerivingFrom method, I wanted to be able to debug the detection, which is – besides from having an explicit implementation – the main reason for this method.

4. Find a Type in an external assembly

If you need a type that lives in an external assembly but you only have it’s Name (or FullName, it’s easy to adapt), this one will resolve the Type for you as long as its assembly is loaded into your AppDomain:

        public static Type? FindType(this string name)
        {
            Type? result = null;
            var nonDynamicAssemblies = AppDomain.CurrentDomain.GetAssemblies().Where(a => !a.IsDynamic);
            try
            {
                result = nonDynamicAssemblies.
                     SelectMany(a => a.GetExportedTypes()).
                     FirstOrDefault(t => t.Name == name);
            }
            catch
            {
                result = nonDynamicAssemblies.
                     SelectMany(a => a.GetTypes()).
                     FirstOrDefault(t => t.Name == name);
            }

            return result;
        }

Conclusion

Like I said in the beginning, this post will be used for future reference. These extensions made a lot of controls, MVVM implementations and business logic I wrote in the past (both for work and my private projects) a whole lot easier. Like always, I hope this post will be helpful for some of you.

Please find the full class in this Gist.

Until the next post, happy coding!

Title Image by Tim Hill from Pixabay

Posted by msicc in Dev Stories, 3 comments
Create scrollable tabs in Xamarin.Forms with CollectionView and CarouselView

Create scrollable tabs in Xamarin.Forms with CollectionView and CarouselView

When it comes to navigation patterns in mobile apps, the tabbed interface is one of the most popular options. While Xamarin.Forms has the TabbedPage (and Shell) to fulfill that need, it lacks one essential feature: scrollable tabs. After studying some of the samples floating around the web and some of the packages that provide such functionality, I tried find an easier solution.

The View

Let’s have a look at the View first. Like you may have guessed from the title, we are using a CollectionView for the tabs and a CarouselView for the Content. This combination makes it quite easy to implement tabs that cover a whole page size or smaller ones within a page.

Here’s the XAML:

<Grid x:DataType="{x:Null}" RowSpacing="0">
    <Grid.RowDefinitions>
        <RowDefinition Height="45" />
        <RowDefinition Height="45" />
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>

    <CollectionView
        x:Name="CustomTabsView"
        Grid.Row="1"
        HorizontalScrollBarVisibility="Never"
        ItemSizingStrategy="MeasureAllItems"
        ItemsSource="{Binding TabVms}"
        ItemsUpdatingScrollMode="KeepItemsInView"
        SelectedItem="{Binding CurrentTabVm, Mode=TwoWay}"
        SelectionMode="Single"
        VerticalScrollBarVisibility="Never">
        <CollectionView.ItemsLayout>
            <LinearItemsLayout Orientation="Horizontal" />
        </CollectionView.ItemsLayout>
        <CollectionView.ItemTemplate>
            <DataTemplate x:DataType="local:TabViewModel">
                <Grid RowSpacing="0">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="*" />
                        <RowDefinition Height="3" />
                    </Grid.RowDefinitions>
                    <Label
                        x:Name="TitleLabel"
                        Grid.Row="0"
                        Padding="15,0"
                        FontAttributes="Bold"
                        FontSize="Small"
                        HeightRequest="50"
                        HorizontalTextAlignment="Center"
                        Text="{Binding Title}"
                        TextColor="White"
                        VerticalTextAlignment="Center" />
                    <BoxView
                        x:Name="ActiveIndicator"
                        Grid.Row="1"
                        BackgroundColor="Red"
                        IsVisible="{Binding IsSelected, Mode=TwoWay}" />
                </Grid>
            </DataTemplate>
        </CollectionView.ItemTemplate>
    </CollectionView>

    <CarouselView
        Grid.Row="2"
        CurrentItem="{Binding CurrentTabVm, Mode=TwoWay}"
        CurrentItemChanged="CarouselView_CurrentItemChanged"
        HorizontalScrollBarVisibility="Never"
        IsScrollAnimated="True"
        IsSwipeEnabled="True"
        ItemsSource="{Binding TabVms}"
        VerticalScrollBarVisibility="Never">
        <CarouselView.ItemTemplate>
            <DataTemplate x:DataType="local:TabViewModel">
                <Grid Margin="10">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="*" />
                    </Grid.RowDefinitions>
                    <Label
                        Grid.Row="0"
                        Margin="10"
                        LineBreakMode="WordWrap"
                        Text="{Binding Content}"
                        VerticalTextAlignment="Center" />
                </Grid>
            </DataTemplate>
        </CarouselView.ItemTemplate>
    </CarouselView>
</Grid>

Let me break that piece down. First, I wrapped everything in a Grid for this sample. The CollectionView of course should be horizontally scrolling but should not show any scroll bar. The tab item template is not a complex one – it is just a Label and a BoxView below it to help with indication of the selection. You are free to make the tab looking whatever you want because of the CollectionView, however.

Below that, we put a CarouselView. For this sample, I just made a simple one with a Lorem Ipsum Label in it on every item.

The ViewModels

Most of you know that I absolutely love the MVVM pattern. And this sample proves me right once again. We need just need two ViewModels to handle scrolling and synchronizing.

The first ViewModel is the TabViewModel:

Snippet

public class TabViewModel : ObservableObject
{
    private string _title;
    private string _content;
    private bool _isSelected;

    public TabViewModel(string title)
    {
        this.Title = title;
        this.Content = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Tempor id eu nisl nunc mi ipsum faucibus vitae aliquet. Turpis egestas integer eget aliquet nibh praesent tristique magna. In fermentum posuere urna nec tincidunt. Vitae congue eu consequat ac felis donec et odio pellentesque. Augue lacus viverra vitae congue. Viverra vitae congue eu consequat. Orci nulla pellentesque dignissim enim sit amet venenatis urna. Et ultrices neque ornare aenean euismod elementum nisi. Id consectetur purus ut faucibus pulvinar. In cursus turpis massa tincidunt. Egestas pretium aenean pharetra magna. Et pharetra pharetra massa massa ultricies mi quis. Nunc sed blandit libero volutpat. Purus viverra accumsan in nisl nisi scelerisque eu ultrices vitae.";
    }

    public string Title { get => _title; set => Set(ref _title, value); }

    public string Content { get => _content; set => Set(ref _content, value); }

    public bool IsSelected { get => _isSelected; set =>Set(ref _isSelected, value); }
}

The TabViewModel in this sample just has the bare minimum, namely the Title, the Content and the IsSelectedFlag to control the Visibility of the Indicator-BoxView. Nothing dramatic so far.

The MainViewModel glues everything together, so let’s have a look:

Snippet

public class MainViewModel : ObservableObject
{
    private TabViewModel _currentTabVm;

    public MainViewModel()
    {

        this.TabVms = new ObservableCollection<TabViewModel>();
        this.TabVms.Add(new TabViewModel("Short Title"));
        this.TabVms.Add(new TabViewModel("A Little Longer Title"));
        this.TabVms.Add(new TabViewModel("An Even Longer Title Than Before"));
        this.TabVms.Add(new TabViewModel("Again Short Title"));
        this.TabVms.Add(new TabViewModel("Mini Title"));
        this.TabVms.Add(new TabViewModel("Different Title"));

        this.CurrentTabVm = this.TabVms.FirstOrDefault();
    }


    public ObservableCollection<TabViewModel> TabVms { get; set; } 

    public TabViewModel CurrentTabVm 
    { 
        get => _currentTabVm;
        set
        {
            Set(ref _currentTabVm, value);
            SetSelection();
        }
    }

    private void SetSelection()
    {
        this.TabVms.ForEach(vm => vm.IsSelected = false);
        this.CurrentTabVm.IsSelected = true;
    }
}

Once again, there is nothing complex in it. We are mocking a collection of TabViewModel and handle the tab selection via Binding. After the current item got selected, we are setting the IsSelected property on it to true to show the Indicator in the CollectionView.

For this sample, I didn’t use a fully blown MVVM framework, so I am setting the BindingContext in the MainPage‘s constructor. The Binding engine in Xamarin.Forms already does almost everything to make this work.

The only thing left is to handle the positioning of the tabs if we are swiping the CarouselView. As this is purely View related, I am using the CurrentItemChanged event in code behind to center the CollectionView‘s selected item:

Snippet

private void CarouselView_CurrentItemChanged(object sender, CurrentItemChangedEventArgs e)
{
    this.CustomTabsView.ScrollTo(e.CurrentItem, null, ScrollToPosition.Center, true);
}

The result of this setup looks like this:

Conclusion

Xamarin.Forms provides a lot of solutions out of the box. Sometimes, however, these are not enough. Luckily, we can combine some of the solutions the framework provides to create fresh solutions within our apps. This post showed one of these. The additional bonus you get with this implementation is the ability to style the tabs in whatever way you want. As always, I hope this post will be helpful for some of you.

Of course, there is also sample for this post on Github.

Until the next post, happy coding, everyone!
Posted by msicc in Dev Stories, Xamarin, 2 comments
Scroll to any item in your Xamarin.Forms CollectionView from your ViewModel

Scroll to any item in your Xamarin.Forms CollectionView from your ViewModel

If you are working with collections in your app, chances are high you are going to want (or need) to scroll to a specific item at some point. CollectionView has the ScrollTo method that allows you to do so. If you are using MVVM in your app however, there is no built-in support to call this method.

My solution

My solution for this challenge consists of following parts:

  • a BindableProperty in an extended CollectionView class to bind the item we want to scroll to
  • a configuration class to control the scrolling behavior
  • a base interface with the configuration and two variants derived from it (one for ungrouped items, one for grouped ones)

Let’s have a look at the ScrollConfiguration class:

public class ScrollToConfiguration
{
    public bool Animated { get; set; } = true;

    public ScrollToPosition ScrollToPosition { get; set; } = ScrollToPosition.Center;
}

These two properties are used to tell our extended CollectionView how the scrolling to the item will behave. The above default values are my preferred ones, feel free to change them in your implementation.

Next, let us have a look at the base interface:

public interface IConfigurableScrollItem
{
    ScrollToConfiguration Config { get; set; }
}

Then we will define two additional interfaces which we are going to use later in our ViewModel:

    public interface IScrollItem : IConfigurableScrollItem
    {
    }

    public interface IGroupScrollItem : IConfigurableScrollItem
    {
        object GroupValue { get; set; }
    }

For a non-grouped CollectionView, we just need to implement IScrollItem. If we have groups, we’ll use IGroupScrollItem to add an object that identifies the group (following the Xamarin.Forms API here).

Extending CollectionView

Let’s connect the dots and implement an extended version of the CollectionView – to do so, create a new class and derive from it. I named mine CollectionViewEx (ingenious, right?).

To wrap things up, we now add a BindableProperty with a PropertyChanged handler to our CollectionViewEx that we can bind against, and which is, most importantly, calling the ScrollTo method of CollectionView.

Here is the full class:

public class CollectionViewEx : CollectionView
{
    public static BindableProperty ScrollToItemWithConfigProperty = BindableProperty.Create(nameof(ScrollToItemWithConfig), typeof(IConfigurableScrollItem), typeof(CollectionViewEx), default(IConfigurableScrollItem), BindingMode.Default, propertyChanged: OnScrollToItemWithConfigPropertyChanged);

    public IConfigurableScrollItem ScrollToItemWithConfig
    {
        get => (IConfigurableScrollItem)GetValue(ScrollToItemWithConfigProperty);
        set => SetValue(ScrollToItemWithConfigProperty, value);
    }

    private static void OnScrollToItemWithConfigPropertyChanged(BindableObject bindable, object oldValue, object newValue)
    {
        if (newValue == null)
            return;

        if (bindable is CollectionViewEx current)
        {
            if (newValue is IGroupScrollItem scrollToItemWithGroup)
            {
                if (scrollToItemWithGroup.Config == null)
                    scrollToItemWithGroup.Config = new ScrollToConfiguration();

                    current.ScrollTo(scrollToItemWithGroup, scrollToItemWithGroup.GroupValue, scrollToItemWithGroup.Config.ScrollToPosition, scrollToItemWithGroup.Config.Animated);

            }
            else if (newValue is IScrollItem scrollToItem)
            {
                if (scrollToItem.Config == null)
                    scrollToItem.Config = new ScrollToConfiguration();

                    current.ScrollTo(scrollToItem, null, scrollToItem.Config.ScrollToPosition, scrollToItem.Config.Animated);
            }
        }
    }
}

Let’s go through the code. The BindableProperty implementation should be common to most of us (if not, read up the docs). The most important part happens in the PropertyChanged handler.

By allowing the value of the BindableProperty to be null, we can reset the item and scroll to the same item again if necessary. Because IScrollItem as well as IGroupScrollItem derive from IConfigurableScrollItem, we can handle them both in one method. To make sure there is a default ScrollToConfiguration, I am checking the Config property for null – in case it is (because I forgot it), there is at least the default. In the end, I am scrolling to the Item in the CollectionView using the ScrollTo method.

The ViewModel(s) and Binding

Here is one of the (simple) ViewModels from the sample application for this post:

public class ItemViewModel : ViewModelBase, IScrollItem
{
    public ItemViewModel()
    {
        this.Config = new ScrollToConfiguration();
    }

    public string Text { get; set; }

    public int Number { get; set; }

    public ScrollToConfiguration Config { get; set; }
}

Now in the parent ViewModel, we just add another property that we can use to bind against the CollectionViewEx‘s ScrollToItemWithConfig property. The Binding is straight forward:

<controls:CollectionViewEx
    Grid.Row="3"
    Margin="6"
    ItemsSource="{Binding ScrollableItems}"
    ScrollToItemWithConfig="{Binding ScrollToVm}"
    SelectedItem="{Binding SelectedItemVm, Mode=TwoWay}"
    SelectionMode="Single">
    <controls:CollectionViewEx.ItemTemplate>
        <DataTemplate>
            <Grid>
                <Label Margin="5,10" Text="{Binding Text}" />
            </Grid>
        </DataTemplate>
    </controls:CollectionViewEx.ItemTemplate>
</controls:CollectionViewEx>

The result of this whole exercise looks like this:

Conclusion

Even if the CollectionView control in Xamarin.Forms provides a whole bunch of optimized functionalities over ListView, there are some scenarios that require additional work. Luckily, it isn’t that hard to extend the CollectionView. Scrolling to a precise ViewModel is easy with the code above. Of course, I created also a sample Github repo.

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

Until the next post, happy coding, everyone!
Posted by msicc in Dev Stories, Xamarin, 1 comment
Xamarin Forms, the MVVMLight Toolkit and I: Command Chaining

Xamarin Forms, the MVVMLight Toolkit and I: Command Chaining

The problem

Sometimes, we want to invoke a method that is available via code for a control. Due to the abstraction of our MVVM application, the ViewModel has no access to all those methods that are available if we would access the control via code. There are several approaches to solve this problem. In one of my recent projects, I needed to invoke a method of a custom control, which should be routed into the platform renderers I wrote for such a custom control. I remembered that I have indeed read quite a few times about command chaining for such cases and tried to implement it. In the beginning, it may sound weird to do this, but the more often I see this technique, the more I like it.

Simple Demo control

For demo purposes, I created this really simple Xamarin.Forms user control:

<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XfMvvmLight.Controls.CommandChainingDemoControl">
  <ContentView.Content>
      <StackLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
            <Label x:Name="LabelFilledFromBehind" Margin="12" FontSize="Large" />   
        </StackLayout>
  </ContentView.Content>
</ContentView>

As you can see, there is just a label without text. We will write the necessary code to fill this label with some text just by invoking a method in the code behind. To be able to do so, we need a BindableProperty (once again) to get our foot into the door of the control:

public static BindableProperty DemoCommandProperty = BindableProperty.Create(nameof(DemoCommand), typeof(ICommand), typeof(CommandChainingDemoControl), null, BindingMode.OneWayToSource);

public ICommand DemoCommand
{
    get => (ICommand)GetValue(DemoCommandProperty);
    set => SetValue(DemoCommandProperty, value);
}

The implementation is pretty straightforward. We have done this already during this series, so you should be familiar if you were following. One thing, however, is different. For this BindableProperty, we are using BindingMode.OneWayToSource. By doing so, we are basically making it a read-only property, which sends its changes only down to the ViewModel (the source). If we would not do this, the ViewModel could change the property, which we do not want here.

Now we have the BindableProperty in place, we need to create an instance of the Command that will be sent down to the ViewModel. We are doing this as soon as the control is instantiated in the constructor:

public CommandChainingDemoControl()
      {
          InitializeComponent();

          this.DemoCommand = new Command(() =>
          {
              FillFromBehind();
          });
      }

      private void FillFromBehind()
      {
          this.LabelFilledFromBehind.Text = "Text was empty, but we used command chaining to show this text inside a control.";
      }

That’s all we need to do in the code behind.

ViewModel

For this demo, I created a new page and a corresponding ViewModel in the demo project. Here is the very basic ViewModel code:

using System.Windows.Input;
using GalaSoft.MvvmLight.Command;

namespace XfMvvmLight.ViewModel
{
    public class CommandChainingDemoViewModel : XfNavViewModelBase
    {
        private ICommand _invokeDemoCommand;
        private RelayCommand _demo1Command;

        public CommandChainingDemoViewModel()
        {
        }

        public ICommand InvokeDemoCommand { get => _invokeDemoCommand; set => Set(ref _invokeDemoCommand, value); }

        public RelayCommand Demo1Command => _demo1Command ?? (_demo1Command = new RelayCommand(() =>
        {
            this.InvokeDemoCommand?.Execute(null);
        }));
    }
}

As you can see, the ViewModel includes two Commands. One is the pure ICommand implementation that gets its value from the OneWayToSource-Binding. We are not using MVVMLight’s RelayCommand here to avoid casting between types, which always led to an exception when I tested the implementation first. The second command is bound to a button in the CommandChainingDemoPage and will be the trigger to execute the InvokeDemoCommand.

Final steps

The final steps are just a few simple ones. We need to connect the  InvokeDemoCommand to the user control we created earlier, while we need to bind the Demo1Commandto the corresponding button in the view. This is the page’s code after doing so:

<?xml version="1.0" encoding="utf-8" ?>
<baseCtrl:XfNavContentPage
    xmlns:baseCtrl="clr-namespace:XfMvvmLight.BaseControls" xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:ctrl="clr-namespace:XfMvvmLight.Controls;assembly=XfMvvmLight"
             x:Class="XfMvvmLight.View.CommandChainingDemoPage" RegisteredPageKey="{Binding CommandChainingDemoPageKey, Source=Locator}">
    <ContentPage.BindingContext>
        <Binding Path="CommandChainingDemoVm" Source="{StaticResource Locator}" />
    </ContentPage.BindingContext>

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>

        <ctrl:CommandChainingDemoControl Grid.Row="0" DemoCommand="{Binding InvokeDemoCommand, Mode=OneWayToSource}" Margin="12"></ctrl:CommandChainingDemoControl>

        <Button Text="Execute Command Chaining" Command="{Binding Demo1Command}" Margin="12" Grid.Row="1" />

    </Grid>
</baseCtrl:XfNavContentPage>

One thing to point out is that we are also specifying the OneWayToSource binding here once again. It should work with normal binding, but it I recommend to do like I did, which makes the code easier to understand for others (and of course yourself). That’s all – we have now a working command chain that invokes a method inside the user control from our ViewModel.

Conclusion

Command chaining can be a convenient way to invoke actions on controls that are otherwise not possible due to the abstraction of layers in MVVM. Once you got the concept, they are pretty easy to implement. This technique is also usable outside of Xamarin.Forms, so do not hesitate to use it out there. Just remember the needed steps:

  • create a user control (or a derived one if you need to call a method on framework controls)
  • add a BindableProperty/DependecyProperty and set its default binding mode to OneWayToSource
  • instantiate the BindableProperty/DependecyProperty inside the constructor of the user control
  • pass the method call/code into the Action part of the newly created Command  instance
  • create the commands in the ViewModel
  • connect the Commands to your final view implementation

Like I wrote earlier, I came across this (again) when I was writing a custom Xamarin.Forms control with renderers, where I had to invoke methods inside the renderer from my ViewModel. Other techniques that I saw to solve this is using Messengers (be it the one from MVVMLight or the Xamarin.Forms Messenger implementation) or the good old Boolean switch implementation (uses also a BindableProperty/DependecyProperty). I decided to use the command chaining approach as it is pretty elegant in my eyes and not that complicated to implement.

The series’ sample project is updated and available here on Github. Like always, I hope this post is useful for some of you.

Happy coding, everyone!


all articles of this series

title image credit

Posted by msicc in Android, Dev Stories, iOS, UWP, Xamarin, 7 comments
Xamarin Forms, the MVVMLight Toolkit and I: taking control over the back buttons

Xamarin Forms, the MVVMLight Toolkit and I: taking control over the back buttons

Why do I need to take control over the back button behavior?

The back button is available on Windows, Android and under certain conditions on iOS. It is one of the key navigation hooks. While the implementations vary between platforms, the functionality is always the same – go back one step in navigation. Sometimes, we need to execute actions before going back, like notifying other parts of our application or even blocking back navigation because we need to perform actions on the local page (think of a WebViewthat has internal navigation capabilities for example). While we do need only a few lines to intercept the hardware back button on Android and UWP, the software back buttons on Android and iOS need some additional code.

Xamarin.Forms – View and ViewModel implementations

Based on the code we have already written in the past posts of this series, we are already able to get events pretty easy into our ViewModel, utilizing the EventToCommandBehavior approach. To get them into our ViewModel, we will throw an own created event. You can do so pretty easy by overriding the OnBackButtonPressed()method every Xamarin.Forms pages come with:

protected override bool OnBackButtonPressed()
{
    base.OnBackButtonPressed();
    BackButtonPressed?.Invoke(this, EventArgs.Empty);
    
    //return true; //breaks navigation    
    return false; //executes navigation
}

Depending on the boolean you will return here, the back navigation will be executed or not. The next step is to pass the event into our ViewModel, like we have done already with the ViewAppearingand ViewDisappearing events before:

private void XfNavContentPage_BindingContextChanged(object sender, EventArgs e)
{
    if (this.BindingContext is XfNavViewModelBase @base)
    {
        this.Behaviors.Add(new EventToCommandBehavior()
        {
            EventName = "Appearing",
            Command = @base.ViewAppearingCommand
        });

        this.Behaviors.Add(new EventToCommandBehavior()
        {
            EventName = "Disappearing",
            Command = @base.ViewDisappearingCommand
        });

        this.Behaviors.Add(new EventToCommandBehavior()
        {
            EventName = "BackButtonPressed",
            Command = @base.BackButtonPressedCommand
        });
    }
}

As you can see from the snippet above, the next step is to add the BackButtonPressedCommandto our base ViewModel. There is nothing special on that, so here we go:

private RelayCommand _backButtonPressedCommand; 

public RelayCommand BackButtonPressedCommand =>
    _backButtonPressedCommand ?? (_backButtonPressedCommand = new RelayCommand(ExecuteBackButtonPressedCommand, CanExecuteBackButtonPressedCommand));

public virtual void ExecuteBackButtonPressedCommand() { }

public virtual bool CanExecuteBackButtonPressedCommand() 
{
    return true;
}

And that’s it, if you just want to get informed or handle back navigation on your own. However, from some of the projects I have worked on, I know that I may need to prevent back navigation. So let’s extend our code to reach that goal as well.

Blocking back navigation

Back in our base class implementation, let’s add a BindableProperty. This boolean property makes it very easy to block the back navigation (no matter if you’re doing so from a ViewModel or a View):

public static BindableProperty BlockBackNavigationProperty = BindableProperty.Create("BlockBackNavigation", typeof(bool), typeof(XfNavContentPage), default(bool), BindingMode.Default, propertyChanged: OnBlockBackNavigationChanged);

private static void OnBlockBackNavigationChanged(BindableObject bindable, object oldvalue, object newvalue)
{
    //not used in this sample
    //valid scneario would be some kind of validation or similar tasks
}

public bool BlockBackNavigation
{
    get => (bool) GetValue(BlockBackNavigationProperty);
    set => SetValue(BlockBackNavigationProperty, value);
}

The next part involves once again the already overridden OnBackButtonPressed() method. If we are blocking the back navigation, we are throwing another event:

protected override bool OnBackButtonPressed()
{
    if (this.BlockBackNavigation)
    {
        BackButtonPressCanceled?.Invoke(this, EventArgs.Empty);
        return true;
    }

    base.OnBackButtonPressed();
    BackButtonPressed?.Invoke(this, EventArgs.Empty);

    if (this.StackState.isModal) 
        return true; 
    else 
    { 
       return false; 
    }
}

Notice that I added an additional step for modal pages. Without that, the hardware button back press code will be executed twice on Android on modal pages. Of course, we are rooting also the BackButtonPressCanceledevent into our ViewModel, so let’s add it to our BindingContextChanged handler:

private void XfNavContentPage_BindingContextChanged(object sender, EventArgs e)
{
    if (this.BindingContext is XfNavViewModelBase @base)
    {
        this.Behaviors.Add(new EventToCommandBehavior()
        {
            EventName = "Appearing",
            Command = @base.ViewAppearingCommand
        });

        this.Behaviors.Add(new EventToCommandBehavior()
        {
            EventName = "Disappearing",
            Command = @base.ViewDisappearingCommand
        });

        this.Behaviors.Add(new EventToCommandBehavior()
        {
            EventName = "BackButtonPressed",
            Command = @base.BackButtonPressedCommand
        });

        this.Behaviors.Add(new EventToCommandBehavior()
        {
            EventName = "BackButtonPressCanceled",
            Command = @base.BackButtonPressCanceledCommand
        });
    }
}

To complete the code, we need to add a boolean property and the BackButtonPressCanceledCommand to our base ViewModel implementation:

private bool _blockBackNavigation;
private RelayCommand _backButtonPressCanceledCommand;

public virtual bool BlockBackNavigation
{
    get => _blockBackNavigation;

    set => Set(ref _blockBackNavigation, value);
}

public RelayCommand BackButtonPressCanceledCommand =>
    _backButtonPressCanceledCommand ?? (_backButtonPressCanceledCommand = new RelayCommand(ExecuteBackButtonPressCanceledCommand, CanExecuteBackButtonPressCanceledCommand));

public virtual void ExecuteBackButtonPressCanceledCommand() { }

public virtual bool CanExecuteBackButtonPressCanceledCommand()
{
    return true;
}

And that’s it. We already implemented everything we need in our Xamarin.Forms project.

Platform implementations

As often, we need to write some platform specific code to make our Xamarin.Forms code work in all scenarios.

Universal Windows Platform

As the Universal Windows Platforms handles the back button globally, no matter if you’re on a PC, tablet or a phone, there’s no need for additional code. Really. It’s already done with the Xamarin.Forms implementation.

Android

For the part of the hardware back button on Android devices, we are already done as well. But Android has also a software back button (eventually), which is in the toolbar (pretty similar to iOS).  There are two options we can use for Android. The first one involves just one line of code in our base page implementation’s constructor:

NavigationPage.SetHasBackButton(this, false);

This will hide the software back button on Android (and iOS as well). It would be perfectly fine on Android because all (phone and tablet) devices have a hardware back button. However, often, we do not have the possibility to go down the easy route. So let’s fully handle the toolbar button. It does not involve a lot of code, and it’s all in the MainActivity class:

protected override void OnPostCreate(Bundle savedInstanceState)
{
    var toolBar = FindViewById<Android.Support.V7.Widget.Toolbar>(Resource.Id.toolbar);
    SetSupportActionBar(toolBar);

    base.OnPostCreate(savedInstanceState);
}


public override bool OnOptionsItemSelected(IMenuItem item)
{
    //if we are not hitting the internal "home" button, just return without any action
    if (item.ItemId != Android.Resource.Id.Home)
        return base.OnOptionsItemSelected(item);

    //this one triggers the hardware back button press handler - so we are back in XF without even mentioning it
    this.OnBackPressed();
    // return true to signal we have handled everything fine
    return true;
}

The first step is to override the OnPostCreate method. Within the override, we are just setting the toolbar to be the SupportActionBar. If we would not do so, the more important override OnOptionsItemSelected would never get triggered. The back button in the toolbar has the internal resource name ‘Home’ (with a value of 16908332). If this button is hit, I am triggering the hardware back button press handler, which will get code execution routed back into the Xamarin.Formscode. By returning true we are telling Android we have handled this on our own. And that’s all we have to do in the Android project.

taking-over-back-button-android

iOS

On iOS,  a custom renderer for our XfNavContentPage is needed to get the same result. I was trying a few attempts that are floating around the web, but in the end this post was the most helpful to reach my goal also on iOS. Here is my version:

[assembly: ExportRenderer(typeof(XfNavContentPage), typeof(XfNavigationPageRenderer))] 
namespace XfMvvmLight.iOS.Renderer 
{ 
    public class XfNavigationPageRenderer : PageRenderer 
    { 
        public override void ViewWillAppear(bool animated) 
        { 
            base.ViewWillAppear(animated); 
  
            //making sure to use this only with non-modal pages 
            if (Element is XfNavContentPage page && this.NavigationController != null) 
            { 
                var thisPageIndex = page.Navigation.NavigationStack.IndexOf(page); 
                if (thisPageIndex >= 1) 
                { 
                    //disabling back swipe complettely: 
                    this.NavigationController.InteractivePopGestureRecognizer.Enabled = false; 
  
                    var backarrowImg = UIImage.FromBundle("arrow-back.png") 
                        .ImageWithRenderingMode(UIImageRenderingMode.AlwaysTemplate); 
  
                    var backButton = new UIButton(UIButtonType.Custom) 
                    { 
                        HorizontalAlignment = UIControlContentHorizontalAlignment.Left, 
                        TitleEdgeInsets = new UIEdgeInsets(11.5f, 0f, 10f, 0f), 
                        //we need to move the image a bit more left to get closer to the OS-look 
                        ImageEdgeInsets = new UIEdgeInsets(1f, -8f, 0f, 0f) 
                    }; 
  
                    //this makes sure we use the same behavior as the OS 
                    //if there is no parent, it must throw an exception because something is wrong 
                    //with the navigation structure 
                    var parent = page.Navigation.NavigationStack[thisPageIndex - 1]; 
                    backButton.SetTitle(string.IsNullOrEmpty(parent.Title) ? "Back" : parent.Title, 
                        UIControlState.Normal); 
  
                    backButton.SetTitleColor(this.View.TintColor, UIControlState.Normal); 
                    backButton.SetImage(backarrowImg, UIControlState.Normal); 
                    backButton.SizeToFit(); 
  
                    backButton.TouchDown += (sender, e) => 
                    { 
                        if (!page.BlockBackNavigation) 
                        { 
                            this.NavigationController.PopViewController(animated); 
                        } 
                        page.SendBackButtonPressed(); 
                    }; 
  
                    backButton.Frame = new CGRect(0, 0, UIScreen.MainScreen.Bounds.Width / 4, 
                        NavigationController.NavigationBar.Frame.Height); 
  
                    var view = new UIView(new CGRect(0, 0, backButton.Frame.Width, backButton.Frame.Height)); 
                    view.AddSubview(backButton); 
  
  
                    var backButtonItem = new UIBarButtonItem(string.Empty, UIBarButtonItemStyle.Plain, null) 
                    { 
                        CustomView = backButton 
                    }; 
  
                    NavigationController.TopViewController.NavigationItem 
                        .SetLeftBarButtonItem(backButtonItem, animated); 
                } 
            } 
        } 
    } 
}

Let me explain the snippet. On iOS, we do not have direct access to the back button events in the navigation bar. We are able to override the back button, though. The first thing we have to make sure is that there is a UINavigationControlleraround. This way, we are still able to use our base page class implementation and its features for modal pages. The next step is to create a button with an image (which needs to be bundled).

Of course, we want the button’s text to behave exactly like the OS one does. That’s why we are going to get the parent view. We can easily use the current view’s NavigationStackindex for that – as long as we do not have cross navigation but a continuous one. In this case, the page before the current page is our parent. If the parent’s Titleproperty is empty, we are setting the title to “Back”, pretty much the same like the OS itself does. If you want it to be empty, just add a Title to the page with ” ” as content. This works also if you do not want your MasterPagein a Xamarin.Forms.MasterDetailPage to have a visible title, btw.

The most important thing to note is the button’s TouchDownevent – which is why we are doing this whole thing. First, we manually navigate back in iOS via the PopViewControllermethod (if necessary). After that, we are once again invoking our Xamarin.Formsimplementation via the SendBackButtonPressed method of the Xamarin.Formspage, which will then trigger our EventToCommandBehavior we implemented earlier.

The last step is to create an UIViewcontainer for the button and assign it as a UIBarButtonItemto the UINavigationController via the SetLeftBarButtonItemmethod the UINavigationItem provides. And that’s it, we now also have control over the back button on iOS.

taking-over-back-button-ios

 

Lust but not least, we need to handle also the swipe-back-gesture. This can be done the hard way by disabling the gesture completelly:

//disabling back swipe complettely:
this.NavigationController.InteractivePopGestureRecognizer.Enabled = false;

I do not have an implementation for handling that in a better way, but I will update the sample with another post in this series later on. At least I have full control over the back navigation, which is (for the moment) all I need.

As always, I hope this post will be helpful for some of you. I also updated the source code of my XfMvvmLight sample on Github to match this blog post. If you have feedback or questions, sound off below in the comments or via my social channels.

Until then, happy coding, everyone!

 

Posted by msicc in Android, Dev Stories, iOS, UWP, Xamarin, 3 comments
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, UWP, Xamarin, 1 comment