Handle Windows Phone back button pressed when using MVVM

When I building an app with a MVVM framework I want to have my view models in a portable class library so I can use same view models for the Windows Phone app as for the iOS and Android app when building apps using Xamarin.

If I press the back button in a Windows Phone app built using MvvmCross (a MVVM framework), the app will be placed in background. That is the behavior I want if I’m on the first view of the app. But if I have navigated to Another view I would like to return to the previous view. To do that I need to handle the back button event. I want to have the code for handle the back button event in my view models that is placed in a portable class library, but the code for handling the event is platform specific. This post will show how to handle the back button event in a Windows Phone app built using WinRT. You can of course use this way to handle platform specific events for other events than the back button event.

First of all I creating an interface, I use to name it IPlatformEvents.

public interface IPlatofrmEvents
{
     event EventHandler BackButtonPressed;
}

In my view models I using IoC (Inversion of Control) and constructor injection to resolve the interface. In my method for handling the back button pressed I using the Close method to close the current view and return to the previous one in the stack. Don’t forget to remove the handler from the event in the handler method. If you do not, it can cause problems later.

public class MyViewModel : MvxViewModel
{
     private IPlatformEvents _platformEvents;     
 
     public MyViewModel(IPlatformEvents platformEvents)
     {
          _platformEvents = platformEvents;
 
          _platformEvents.BackButtonPressed += BackButtonPressed;
     }
 
     void BackButtonPressed(object sender, EventArgs e)
     {
         Close(this);
         _platformEvents.BackButtonPressed -= BackButtonPressed;
     }
}

If you want to read more about IoC and MvvmCross you can read my post about MvvmCross and IoC and my post about how to use a different IoC provider then the built-in when using MvvmCross.

Next step is to write the platform specific implementation of the interface. For an Windows Phone app using WinRT the implementation would look like this.

public class WindowsPhoneEvents : IPlatformEvents
{
      public event EventHandler BackButtonPressed;
 
      public WindowsPhoneEvents()
      {
          HardwareButtons.BackPressed += HardwareButtons_BackPressed;
      }
 
      void HardwareButtons_BackPressed(object sender, BackPressedEventArgs e) 
      { 
             if(BackButtonPressed != null) 
             { 
                   BackButtonPressed(this, new EventArgs()); 
                   e.Handled = true; 
             } 
      } 
 
}

When I have created the platform specific implementation I just have to register it to the IoC container. You can read how to do it if you read the post that I linked to above.

Use a different IoC provider then the built-in when using MvvmCross

As I wrote in an earlier post, MvvmCross has IoC built-in. If you for some reason want to use a different IoC provider then the built-in when using MvvmCross you can write your own provider and use for example Autofac or TinyIoC. This post will show you how to use Autofac.

I create an own project (of type Portable Class Library) so I don’t have to reference Autofac in my core project. I can have it in my client project but then I can’t reference it from other client projects.

In my Autofac resolver I have to implement the IMvxIoCProvider interface and the class has to inherit from MvxSingleton if it should work.

public class AutofacMvxProvider : MvxSingleton<IMvxIoCProvider>, IMvxIoCProvider
    {
        private IContainer _container;
 
        public AutofacMvxProvider(IContainer container)
        {
            _container = container;
        }
 
        public void CallbackWhenRegistered(Type type, Action action)
        {
            _container.ComponentRegistry.Registered += (sender, args) => {
                if(args.ComponentRegistration.Services.OfType<TypedService>().Any( x => x.ServiceType == type))
                {
                    action();
                }
            };
        }
 
        public void CallbackWhenRegistered<T>(Action action)
        {
            CallbackWhenRegistered(typeof(T), action);
        }
 
        public bool CanResolve(Type type)
        {
            return _container.IsRegistered(type);
        }
 
        public bool CanResolve<T>() where T : class
        {
            return _container.IsRegistered<T>();
        }
 
        public object Create(Type type)
        {
            return Resolve(type);
        }
 
        public T Create<T>() where T : class
        {
            return Resolve<T>();
        }
 
        public object GetSingleton(Type type)
        {
            return Resolve(type);
        }
 
        public T GetSingleton<T>() where T : class
        {
            return Resolve<T>();
        }
 
        public object IoCConstruct(Type type)
        {
            return Resolve(type);
        }
 
        public T IoCConstruct<T>() where T : class
        {
            return Resolve<T>();
        }
 
        public void RegisterSingleton(Type tInterface, Func<object> theConstructor)
        {
            var builder = new ContainerBuilder();
            builder.Register(x => theConstructor()).As(tInterface).AsSelf().SingleInstance();
            builder.Update(_container);
        }
 
        public void RegisterSingleton<TInterface>(Func<TInterface> theConstructor) where TInterface : class
        {
            var builder = new ContainerBuilder();
            builder.Register(x => theConstructor()).As<TInterface>().AsSelf().SingleInstance();
            builder.Update(_container);
        }
 
        public void RegisterSingleton(Type tInterface, object theObject)
        {
            var builder = new ContainerBuilder();
            builder.RegisterInstance(theObject).As(tInterface).AsSelf().SingleInstance();
            builder.Update(_container);
        }
 
        public void RegisterSingleton<TInterface>(TInterface theObject) where TInterface : class
        {
            var builder = new ContainerBuilder();
            builder.RegisterInstance(theObject).As<TInterface>().AsSelf().SingleInstance();
            builder.Update(_container);
        }
 
        public void RegisterType(Type tFrom, Type tTo)
        {
            var builder = new ContainerBuilder();
            builder.RegisterType(tTo).As(tFrom).AsSelf();
            builder.Update(_container);
 
        }
 
        public void RegisterType(Type t, Func<object> constructor)
        {
            var builder = new ContainerBuilder();
            builder.Register(x => constructor()).As(t).AsSelf();
            builder.Update(_container);
        }
 
        public void RegisterType<TInterface>(Func<TInterface> constructor) where TInterface : class
        {
            var builder = new ContainerBuilder();
            builder.Register(x => constructor()).As<TInterface>().AsSelf().SingleInstance();
            builder.Update(_container);
        }
 
        public void RegisterType<TFrom, TTo>()
            where TFrom : class
            where TTo : class, TFrom
        {
            var builder = new ContainerBuilder();
            builder.RegisterType<TTo>().As<TFrom>().AsSelf();
            builder.Update(_container);
        }
 
        public object Resolve(Type type)
        {
            return _container.Resolve(type);
        }
 
        public T Resolve<T>() where T : class
        {
            return _container.Resolve<T>();
        }
 
        public bool TryResolve(Type type, out object resolved)
        {
            return _container.TryResolve(type, out resolved);
        }
 
        public bool TryResolve<T>(out T resolved) where T : class
        {
            return _container.TryResolve<T>(out resolved);
        }
    }

To use your own IoC provider you have to override the CreateIocProvider method in your Setup class.

protected override Cirrious.CrossCore.IoC.IMvxIoCProvider CreateIocProvider()
{
            var builder = new ContainerBuilder();
 
            builder.RegisterType<MyType>().As<IMyType>();
 
            var container = builder.Build();
 
            return new AutofacMvxProvider(container);
}

MvvmCross and IoC

MvvmCross has IoC built-in, in this post I will show how to use it. The Mvx class has a few static metods that you can use for configure the IoC container.

RegisterType
Use the register type method to register new types to the IoC container.

//Register a type
Mvx.RegisterType<IMyType,MyType>();
 
//Register a type as a singleton
Mvx.RegisterSingleton<IMySingleton>(new MySingleton());

Resolve
Use the resolve method to create instances from interfaces.

var myInstance = Mvx.Resolve<IMyType>();

But I recommend to use constructor injection so much as possible. This because you can use it to resolve interfaces in a class library without referencing the assembly that contains the IoC container.

public class MyClass
{
     private IMyType _myType;
 
     public MyClass(IMytype myType)
     {
          _myType = myType;
     }
}

I use to register types in the create app method in the Setup class on each client.

 

 

Xamarin, MvvmCross and ValueConverters

This post will show you how to use ValueConverters in an Android app built with Xamarin. For an introduction to Xamarin and MvvmCross read this blog post.

If you want to convert values before you bind it to a property you can use value converters. For example if you want to view an element if a string property is not null you can use a value converter to convert it to a boolean.

First you will create a new class that inherit from MvxValueConverter and then override the Convert method and write your code there, in this case a simple null check. The class has to name end with ValueConverter.

public class IsNullValueConverter : MvxValueConverter
{
    public override object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
          return !String.IsNullOrEmpty(value as string);
    } 
}

Then name of the value converter will be the first part of the class name, so my IsNullValueConverter class will be used as IsNull in the Android Xml and the argument will be the property in your view model that you want to use.

<LinearLayout
        android:orientation="horizontal"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        local:MvxBind="Visible IsNull(Name)">

ValueConverters is a very effective and simple way to do convert values without to create a property in the ViewModel for it.

Xamarin and MvvmCross

A way to reuse so much code as possible when working with Xamarin is to use the MVVM-pattern. A framework for MVVM that works great with Xamarin is MvvmCross (http://www.nuget.org/packages/MvvmCross/3.1.1). This blog post shows the basics about to use MvvmCross with Xamarin for an Android app. More posts about Xamarin and MvvmCross will come later.

Installation
The easiest way to install MvvmCross is to installed above mention NuGet package. In order to share as much code as possible I have the code for ViewModels in a core project (a Portable Class Library) that I can share with apps for iOS and Windows. I have to install the NuGet package on both the client project and the core project.

ViewModels
Viewmodels will be shared over all platforms. The ViewModel has to inherit MvxViewModel and override the start method. In the start method, put the code that will run when the viewmodel is initialized. In my example I get the data there.

Each property needs to have a private variable that contains the data. But when you are setting the data you need to set the property, not the variable. The get method of the propery will just return the value of the variable. The set method also needs to call RaisePropertyChanged to notify the UI that the value of the property has changed.

private ISettings _settings;
 
public SettingsViewModel (ISettings settings)
{
            _settings = settings;
}
 
public async override void Start()
{
	base.Start ();
        Settings = _settings.GetSettings(); 
}
 
private List<Setting> _settingsList
public List<Setting> Settings
{
        get
        {
             return _settingsList;   
        }
        set 
        {
              _settingsList = value;
               RaisePropertyChanged(() => Settings);
        }
}

Databinding and views
Code to databind will be platform specific. For Android the binding code will be placed in the layout files.

To use bindings in your layout files you need to add a namespace to your root element.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:local="http://schemas.android.com/apk/res-auto">

The easiest way to create a list of bound items is to use MvxListItem. Use the MvxBind property to set the item source for the list and MvxItemTemplate to set which template that will be used for each item.

<Mvx.MvxListView
        local:MvxBind="ItemsSource Settings"
        local:MvxItemTemplate="@layout/settings_item" />

In the layout file for the list item use MvxBind to bind text to the elements.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:local="http://schemas.android.com/apk/res-auto">
        <TextView
            local:MvxBind="Text Key" />
        <TextView
            local:MvxBind="Text Value" />
</LinearLayout>

The Activity
Your activity need to inherit MvxActivity and override the ViewModel property. Notice the new keyword in the property head.

public new SettingsViewModel ViewModel
{
       get { return base.ViewModel as SettingsViewModel; }
       set { base.ViewModel = value; }
}

That is the only thing you need to do in your activity to start using MvvmCross.

Splash screen
When your app is starting it could be nice to show a splash screen to your users. MvvmCross makes it easier for you to do that. When you install MvvmCross you will get files in your project for a splash screen. Just edit it so it looks the way you want.

The splash screen will not be the main launcher, set the first activity to be the main launcher. If you have two main launchers the app will be in the app list twice.

That was the basics about MvvmCross, more to come in future posts.