Dot.Blog

C#, XAML, Xamarin, UWP/Android/iOS

Windows Phone et Android en mode Xamarin.Forms avec Mvvm Light

[new:30/04/2015]Windows Phone, Android, iOS, les Xamarin.Forms et Mvvm Light portable, en voici un joli cocktail ! Comment associer tout cela pour développer des apps portables avec un code et une UI 100% unique et partagé ? C’est ce que je vous propose de voir…

Un code, une UI, trois plateformes

Grâce aux Xamarin.Forms que j’ai déjà présentées longuement et qui ajoutent une couche UI portable mais native à Xamarin il est possible d’écrire vraiment pour la première fois un code + son UI en mode totalement portable. Mieux ce code et cette UI sont parfaitement UNIQUES sans duplication, sans #if sans rien. Un code, une UI, trois plateformes.

Le développement pour mobiles n’a jamais été aussi clair, simple et rapide. Mais je vous ai présenté ces nouveautés au fil de leurs arrivées. De même que MVVM Light qui est devenu portable et surtout qui aujourd’hui s’adapte parfaitement aux Xamarin.Forms.

Pourquoi cet article ? Parce que toutes ces nouveautés avaient besoin de murir et de se stabiliser pour véritablement pouvoir commencer à dégager des stratégies de développement utilisables en production. Les Xamarin.Forms par exemple ont connu quelques breaking changes dans le courant de l’année mais aussi des améliorations essentielles. Elles arrivent désormais dans une phase mature. Mvvm Light portable a aussi connu quelques ajustement entre sa toute première version et aujourd’hui.

Bref, tout ce petit monde augurait un avenir radieux mais encore fallait-il que tout cela se stabilise. Ce nouveau plateau est atteint et on peut désormais parler production au lieu de tests pour early adopters.

Ménage à trois

Il en aura fallu du temps pour qu’une plateforme de développement et des outils puissants soient disponibles pour véritablement permettre ce mariage à trois entre trois plateformes que tout oppose, que tout sépare, by design et surtout par volonté commerciale de dresser des murs infranchissables pour rendre captif les clients autant que les éditeurs et développeurs.

Heureusement le monde Microsoft est certainement et contre toute attente le moins fermé de tous… Apple avec iOS reste tellement hermétique qu’il faut absolument un Mac même pour lancer une émulation de iPhone… C’est aussi pour cela que je préfère me concentrer sur les 2 seules plateformes intéressantes du marché : Windows Phone car c’est le meilleur OS techniquement parlant doté du meilleur tooling, et Android car c’est le numéro 1 qui contrôle presque tout le marché et qu’il semble difficile d’ignorer le vainqueur tout comme il a été difficile d’ignorer Windows dans les 20 dernières années.

Ouverture car seulement dans le monde Microsoft a pu naitre quelque chose comme Xamarin basé sur Mono : une plateforme .NET open source tournant sur PC, Mac et Linux. Et cela n’a été possible que parce que Microsoft a ouvert dès le départ le framework .NET.

Ouverture encore car Microsoft au lieu d’ignorer ou de combattre Xamarin en a fait un allié. Et c’est une excellente chose. Pour Windows Phone en premier. Car si cela ne coute pas plus cher de développer une app qui tourne _aussi_ sur Windows Phone, pourquoi s’en priver ? Au lieu de prendre un SDK Google et de ne développer que pour l’OS majoritaire (ce qui est logique pour un éditeur) on peut avec un même code créer des apps qui marchent _aussi_ sur Windows Phone et iOS. Il faudrait être fou pour choisir une plateforme de développement restrictive lorsqu’on a la possibilité d’écrire 3 apps en même temps avec un seul code ! Et cela est bon pour Windows Phone. Car puisqu’il ne coute rien de supporter WP, autant choisir de le faire plutôt que d’ignorer cet OS apprécié par tous ses utilisateurs.

Le mobile impose les mêmes bonnes pratiques

Développer une app pour mobile c’est avant tout développer une application, un logiciel, appelez cela comme vous le voulez mais il s’agit de la même chose. Et ce qui est bon pour un logiciel WPF en mode desktop sur PC s’applique à l’identique à une app mobile.

Notamment les bonnes pratiques de développement comme la séparation des tiers, l’écriture d’un code à faible couplage, etc, tout cela reste parfaitement valable !

Pour cela il faut une méthode. MVVM en est une. Mais MVVM ne s’applique à merveille qu’au couple C#/XAML qui n’est en réalité disponible seulement que sur Windows Phone (dans le monde des mobiles)…

C’est sans compter sur Xamarin.Forms qui amène XAML (un sous-ensemble) sur iOS et sur Android ! XAML et sa syntaxe, sa logique, et son databinding qui est à la base même de MVVM.

Armé des Xamarin.Forms ne reste plus qu’à ajouter un framework comme MVVM Light pour recréer un environnement de travail semblable à celui qu’on utilise pour les “grosses” applications sur PC.

C#, XAML, le framework .NET, Xamarin, Xamarin.Forms, Mvvm Light, un ensemble unique qui recréée les conditions idéales pour développer avec le même savoir (et le même savoir-faire) sur tous les OS mobiles qui comptent.

Reste à comprendre comment utiliser toutes ces briques en même temps dans un tout cohérent pour enfin développer librement des apps _natives_ pour les 3 OS mobiles avec un seul code et une seule UI.

C’est exactement ce que je vais vous montrer maintenant. Alors let’s go !

Xamarin Studio ou Visual Studio ?

Visual Studio est malgré l’EDI le plus sophistiqué et le plus complet auquel on peut adjoindre des outils comme ReSharper par exemple. C’est aussi le seul environnement permettant de travailler sur les 3 cibles à la fois. Lorsqu’on créée une nouvelle solution cross-plateforme Xmarin.Forms seul Visual Studio sait créer outre le projet de code partagé les trois projets pour chaque cible. Ne serait-ce que pour supporter Windows Phone et Android il est donc préférable d’utiliser Visual Studio tout simplement parce qu’il sait le faire !

Xamarin Studio a beaucoup évolué et ressemble beaucoup à VS mais il se concentre sur ces cibles à lui : Android, Mac, Mono. Pas de projet Windows Phone notamment. Mais si on vise une app uniquement Android tout en bénéficiant de C#, XAML et du reste, alors Xamarin Studio est un très bon choix. Avec l’avantage d’être portable et d’exister sur Mac ou Linux.

Dans la suite de cet article j’utiliserai donc Visual Studio pour les raisons évoquées ici car la solution que je vais vous montrer couvre Windows Phone et Android (iOS aussi mais je n’ai pas de Mac pour l’émulateur en ce moment).

Bien entendu tout ce qui est montré ici nécessite un setup complet : Visual Studio 2013 service pack 4, une machine Windows 8.1 Pro ou Entreprise pour faire tourner la partie Windows Phone, le SDK de ce dernier et bien sûr une licence Xamarin installée et tout le SDK Android. Cela fait beaucoup mais c’est le prix du ticket d’entrée pour faire du cross-plateforme…

Etape 1 – Créer la solution

Sous Visual Studio donc on fait comme d’habitude pour créer un nouveau projet. C’est lorsqu’arrive le dialogue de création qu’il faut choisir la bonne solution :

XFML-CreateProject

Il faut farfouiner dans la liste de gauche pour trouver “Mobile Apps” et c’est alors qu’à droite on trouve trois possibilités liés aux Xamarin.Forms. Soit une librairie de classes, mais cela ne nous intéresse pas ici, soit un projet vide (Blank App) mais sous deux formes possibles : PCL ou SAP.

PCL ou SAP ?

En mode Shared Assets Project il existe trois projets mobiles, un pour chaque plateforme, et un projet contenant du code partagé. Ce code est compilé par chaque application mobile. C’est un mode un peu enquiquinant car les trois plateformes n’ont pas tout à fait le même framework .NET même s’ils sont très proches. Le code partagé appartenant aux trois projets à la fois il faut faire attention à ce qu’on écrit pour éviter des incompatibilités. En revanche ce code partagé est systématiquement natif puisque lié et compilé par chaque projet cible. Il est donc possible d’utiliser directement des #if pour déterminer la plateforme et écrire du code ultra spécifique pour l’une ou l’autre des cibles.

En mode PCL c’est Visual Studio (ou Xamarin Studio) qui s’occupe de créer une sorte de “vue” sur l’intersection des frameworks sélectionnés. De fait un tel projet partagé en mode PCL est avant tout une DLL compilée pour elle-même et validée en permanence pour “rester les clous” de l’ensemble d’OS choisi au départ (le “profil .net”).

Ce mode est avantageux en cela qu’il permet d’être certain de bénéficier d’un framework totalement commun à toutes les cibles rendant l’écriture plus fiable et plus aisée. En revanche puisque la PCL est compilée pour elle-même il est impossible “out of the box” d’y écrire du code spécifique à l’une ou l’autre des plateformes cibles. Heureusement il existe des façons de contourner cette limitation et ces moyens sont plutôt plus “propres” que de farcir son code de #if. Xamarin offre un moyen simple par exemple de retourner des valeurs qui dépendent de la plateforme et ce même dans du code Xaml. Peu importe les noms de méthodes ou les astuces il s’agit en réalité du principe que je proposais dans ma vidéo sur l’injection de code natif dans un projet cross-plateforme. On peut (re)visualiser cette vidéo (qui utilise MvvmCross et Xamarin) pour comprendre le mécanisme utilisé.

Bref, pour pas mal de raisons il est donc souvent préférable d’utiliser une PCL plutôt que le mode SAP. Toutefois comme d’habitude c’est au développeur de faire son choix en fonction des contraintes du projet à développer.

Ici j’utiliserai donc un projet “Blank App (Xamarin.Forms Portable” c’est à dire en mode PCL.

La solution de base

Comme le montre la figure ci-dessous la solution créée par VS contient bien 4 projets, le projet PCL et les trois projets des plateformes supportées (Android, iOS et Windows Phone).

XFML-CreateProject2

Le nom de la solution qu’on retrouve dans chaque projet est XFML pour Xamarin.Forms et Mvvm Light. On donne les noms qu’on peut… Le projet où ce nom est tel quel est le projet commun, la PCL, les autres projets ajoutent au nom de base les extensions “.Droid” pour Android, “.iOS” pour… iOS et .WinPhone pour Windows Phone.

On notera que pour l’instant le mécanisme utilise Windows Phone 8.0 c’est à dire en mode “Silverlight” et non du 8.1 mais cela viendra certainement à moins que Xamarin ne prépare directement le passage à Windows 10. Mais le code compilé tourne bien entendu sur Windows Phone 8.1.

Lorsque la solution se créée, comme elle ajoute le projet iOS systématiquement Xamarin qui tourne derrière réclame la connexion à la machine Mac où sera exécuté l’émulateur. On peut ignorer tout cela et supprimer à la fin du cycle le projet iOS, ce que j’ai fait.

Mettre à jour les packages

Avant de commencer à coder il est préférable de s’assurer que tous les packages installés sont à jour. Le template de solution n’utilise pas forcément les dernières versions de Xamarin.Forms notamment.

La technique est simple, il suffit de visiter les références de chaque projet (tous) de faire un clic droit et d’accéder à la gestion des paquets Nuget puis de regarder à gauche dans la rubrique “mises à jour” et d’installer toutes celles qui sont disponibles.

XFML-CreateProject3-NugetUpdate

Mise à jour de la PCL

XFML-CreateProject3-NugetUpdateDroid

Mise à jour du projet Android

XFML-CreateProject3-NugetUpdateWP

Mise à jour du projet Windows Phone

On peut faire plus simple en faisant un clic droit sur la solution et en demandant “Manage Nuget Package for Solution”. Ici il est possible de faire les mêmes opérations mais pour tous les projets à la fois avec même la possibilité de sélectionner ceux qui devront être manipulés… Pourquoi faire compliqué lorsqu’il existe des astuces de ce genre !

Installer MVVM Light

C’est d’ailleurs en passant par la solution et sa gestion globale des paquet Nuget que je vais installer MVVM Light pour tous les projets à la fois.

XFML-D-Add Mvvm Light

On choisit la rubrique “online”, on cherche “mvvm light” et on installe “MVVM Light Libraries only”. Nous n’avons en effet pas besoin de tout le template habituellement installé, seules les librairies de base sont nécessaires (pour chaque projet donc).

XFML-D-Add Mvvm Light2

Le choix des projets impactés quand on fait des opérations sur les paquets Nuget via la Solution

MVVM Light Portable

En devenant portable MVVM Light a du s’adapter un peu. Même si ce n’est pas l’article pour discuter en profondeur de ces changements il faut noter au moins qu’il n’y a pas par défaut de ViewModelLocator par exemple.

Ce dernier n’est pas absolument nécessaire mais il permet une séparation du code que j’aime bien, nous recréerons cette classe dans quelques instants.

Il faut aussi savoir que MVVM Light Portable s’il propose toujours sa gestion d’IoC (qui peut d’ailleurs être remplacée facilement) se fonde désormais sur un code de Patterns & Practices de Microsoft : le CommonServiceLocator qui autorise une unification de l’accès aux conteneurs d’IoC. Cela permet à tous les frameworks qui utilisent cette interface de proposer différents conteneurs tout en maintenant un code unifié et similaire puisque traité par l’interface commune. En suivant le lien précédent vous accèderez au projet CodePlex de cette librairie.

Sinon pour l’essentiel MVVM Light fonctionne toujours de la même façon et surtout selon les mêmes principes pour les mêmes objectifs. Le lecteur intéressé pourra se plonger dans le livre gratuit “Méthodes & Frameworks MVVM” qu’on retrouve sur la page des livres de la collection “ALL DOT BLOG”.

Etape 2 – Créer le code de l’app !

La solution est créée, les paquets Nuget de tous les projets sont à jour et nous avons installé MVVM Light dans chacun d’entre eux. La place est nette et propre et on peut se lancer dans l’écriture du code !

La page principale

Dans le projet PCL, le seul que nous toucherons dans cet article, nous créons une nouvelle page XAML : Add new item / Form XAML Page.

Cela créée une fiche XAML spécifique à Xamarin.Forms. A la fois il s’agit de quelque chose de très familier, un code XAML et son code behind en C# comme on le retrouve sous WPF, et à la fois il y a une différence de taille : il n’y a pas pour l’instant de designer visuel pour ce sous-ensemble de XAML. On peut d’ailleurs choisir de tout faire en code C# sans utiliser XAML. Mais ce dernier est bien plus clair pour spécifier une interface utilisateur que la création imbriquée d’instances de classe dans un code C#.

J’étais convaincu à la sortie de Xamarin qu’un portage XAML serait un rêve, j’oubliais que ce rêve n’est total qu’avec Blend ou Visual Studio et leur concepteur visuel ! Mais le rêve est déjà à moitié réalisé et en cross-plateforme. Nul doute que Xamarin ne s’arrêtera pas en si bon chemin. Mais pour l’instant il faut un peu de pratique WPF ou Silverlight pour concevoir toute une UI en XAML. Sinon reste le mode C# donc.

Ici en vieux routier de XAML j’opte pour cette solution mais ce n’est qu’un choix personnel même s’il se fonde sur les qualités indéniables de XAML pour décrire une UI.

XFML-E-XamlXF MainView

Il n’y a pas grand chose dans cette “MainView” (puisque tel est le nom que je lui ai donné) ! juste une ContentPage vide, un contrôle des Xamarin.Forms communément utilisé pour créer des pages contenant tout ce qu’on veut.

On note que l’entête de ce XAML est plus expéditif que celui d’un XAML WPF ou Silverlight ce qui permet rapidement de faire la différence.

Comme nous n’avons pas de ViewModel pour l’instant, nous laissons ce code de base en place et nous y reviendrons plus tard pour créer l’UI.

ViewModelLocator

Toute la logique d’une application se concentre dans des classes de service et dans les ViewModels lorsqu’on travaille en suivant MVVM. Les Xamarin.Forms permettent de travailler en suivant ce pattern sans obliger la présence d’un framework supplémentaire.

J’ai déjà expliqué dans un vieil article comment implémenter du code suivant MVVM sans utiliser de framework particulier pour montrer les principes fondateurs de ce pattern. Dans une petite app il est évident qu’on peut même être tenté de tout faire dans le code behind de la page XAML… après tout… Mais je ne vais pas refaire le match ! C’est un sujet éculé sur lequel j’ai tellement écrit qu’on considèrera qu’utiliser un framework comme MVVM Light, justement “light”, reste la meilleure approche. Pour ceux qui resteraient dubitatif je ne peux que les renvoyer à tous mes articles sur MVVM (et ça fait de la lecture !).

Pour commencer nous allons créerons un répertoire “ViewModel” dans le projet PCL. Et dans ce répertoire nous allons ajouter une nouvelle classe “MainViewModel” puisque la vue s’appelle “MainView”. Pour l’instant nous laissons cette classe vide.

Créons maintenant toujours dans le répertoire ViewModel la classe “ViewModelLocator”.

C’est ici que nous allons recréer le ViewModel locator de MVVM Light qui n’apparait pas dans la version Portable. Non pas que cette indirection soit nécessaire techniquement parlant mais tout simplement parce qu’il semble toujours légitime dans l’esprit MVVM d’éviter tous les couplages forts. Ainsi ce locator va isoler les vues de la création des instances des ViewModels. Là encore inutile de refaire le débat sur la notion de couplage faible qui est devenue l’une des bases de la programmation “moderne”. Acceptons que c’est une bonne pratique, les plus curieux pourront lire milles choses sur la question pour se faire leur opinion.

Voici le ViewModelLocator de notre application, recréé au plus proche de son équivalent sous MVVM Light “classique” :

using GalaSoft.MvvmLight.Ioc;
using Microsoft.Practices.ServiceLocation;

namespace XFML.ViewModel
{
    /// <summary>
    /// This class contains static references to all the view models in the
    /// application and provides an entry point for the bindings.
    /// </summary>
    public class ViewModelLocator
    {
        /// <summary>
        /// Initializes a new instance of the ViewModelLocator class.
        /// </summary>
        public ViewModelLocator()
        {
            ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);

            ////if (ViewModelBase.IsInDesignModeStatic)
            ////{
            ////    // Create design time view services and models
            ////    SimpleIoc.Default.Register<IDataService, DesignDataService>();
            ////}
            ////else
            ////{
            ////    // Create run time view services and models
            ////    SimpleIoc.Default.Register<IDataService, DataService>();
            ////}

            SimpleIoc.Default.Register<MainViewModel>();
        }

        public MainViewModel Main
        {
            get
            {
                return ServiceLocator.Current.GetInstance<MainViewModel>();
            }
        }

        public static void Cleanup()
        {
            // TODO Clear the ViewModels
        }
    }
}

 

Ce qui ne change pas : l’utilisation de SimpleIoc pour enregistrer les ViewModels dans le conteneur et la création d’une propriété par ViewModel. L’obtention de l’instance par le biais du conteneur d’IoC est tout aussi classique tout comme la présence d’une méthode CleanUp dans laquelle on peut placer du code de nettoyage pour supprimer les ViewModels en mémoire par exemple.

Ce qui change : L’utilisation de Microsoft.Practices.ServiceLocation qui permet d’isoler le conteneur d’Ioc utilisé (ici SimpleIoc) en fournissant une interface commune non dépendante de la classe réellement instanciée. Dans le même esprit, sinon cela ne servirait à rien, c’est par le biais de ce ServiceLocator qu’on obtient les instances des ViewModels, SimpleIoc étant alors totalement gommé même si c’est lui qui effectue le travail. On peut ainsi envisager de changer ce conteneur par un autre à tout moment sans aucun impact sur le code.

Le ViewModel

Il est temps de revenir à notre ViewModel. Nous avions créé juste une classe vide pour pouvoir la référencer dans le ViewModelLocator selon le vieux principe de qui de la poule ou de l’oeuf etc..

Pour cet exemple je vais donner dans le simple, le Hello World de MVVM : un bouton et un label, quand on clique sur le bouton le label indique combien de fois on a cliqué. C’est simple mais cela permet de mettre en évidence les mécanismes de base comme la création d’un ViewModel, son héritage, les propriétés public avec le support de l’INPC (notification de changement), la création de commande, les bindings… Beaucoup de choses dans peu de code, c’est toujours une bonne pratique !

Voici le code de MainViewModel :

using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;

namespace XFML.ViewModel
{
    public class MainViewModel : ViewModelBase
    {
        private int count;

        public int Count
        {
            get { return count; }
            set
            {
                if (Set(()=>Count,ref count,value))
                    RaisePropertyChanged(()=>FormattedCount);
            }
        }

        public string FormattedCount
        {
            get { return string.Format("Vous avez cliqué {0} fois", Count); }
        }

        private RelayCommand clickCommand;

        public RelayCommand ClickCommand
        {
            get
            {
                return clickCommand ??
                       (clickCommand = new RelayCommand(() => Count++));

            }
        }

    }
}

 

On notera :

  • L’héritage de la classe mère ViewModelBase, un classique sous MVVM Light. Cela offre de nombreux services essentiels pour le support de MVVM comme l’INPC (petit non de l’interface INotifyPropertyChanged).
  • La création d’une propriété pour le compte et une autre qui reprend la valeur entière pour la formater en chaîne. Ce qui permet de voir comment notifier le changement d’une propriété dérivée.
  • La création d’une commande qui gèrera le clic sur le bouton.

 

Il n’y a rien d’exotique ici, que des choses classiques sous MVVM Light.

Et c’est cela qui est important : la proximité est telle avec un code “desktop” qu’on peut même envisager de reprendre des morceaux d’une application WPF ou Silverlight pour les porter presque sans effort sous Android, Windows Phone et iOS !

J’ai peut-être gardé une âme d’enfant mais je m’émerveille devant une telle portabilité, une telle identité, une qualité similaire entre ce code destiné à tourner sur un mobile et un code WPF pour gros PC de bureau. Sachant qu’ici ce code est écrit une seule fois pour tourner sur trois plateformes qui n’ont vraiment rien en commun.

Bref notre VM est maintenant complet et va fonctionner aussi bien sur un iPhone qu’un Nexus ou un Lumia et on pourrait même le réutiliser tel quel pour du WinRT ou du WPF. Pas mal quand même.

Mais ça c’était déjà vrai avec Xamarin sans les Xamarin.Forms. Ce qui manquait cruellement c’était l’unification des UI justement. Faire des ViewModel portables on le faisait avec MvvmCross par exemple (et Xamarin). Passons donc à ce qui fait tout l’intérêt des Xamarin.Forms, l’unification des UI.

App.cs

Juste avant de passer au visuel, il nous reste un petit détail à régler dans le App.cs.

D’abord cette classe doit descendre de Application qui est fournie avec les Xamarin.Forms, le template doit le faire mais il faut s’en assurer.

Ensuite nous allons créer une propriété statique pour exposer le ViewModelLocator. La classe App est un bon endroit pour cela car il y a forcément une et une seule instance pour toute application Xamarin.Forms on est donc certain de pouvoir s’y référer.

Enfin dans le constructeur de App nous devons charger la fiche principale de l’application, notre MainView ici.

Ce qui donne ce code très simple mais important :

using Xamarin.Forms;
using XFML.ViewModel;

namespace XFML
{
    public class App : Application
    {

        private static ViewModelLocator locator;

        public static ViewModelLocator Locator
        {
            get { return locator ?? (locator = new ViewModelLocator()); }
        }
        public App()
        {
            // The root page of your application
            MainPage = new MainView();
        }

        protected override void OnStart()
        {
            // Handle when your app starts
        }

        protected override void OnSleep()
        {
            // Handle when your app sleeps
        }

        protected override void OnResume()
        {
            // Handle when your app resumes
        }
    }
}

 

Le visuel

L’application est presque terminée, reste la partie visuelle portable. Mais qui dit MVVM, qui dit MVVM Light dit forcément databinding.

Or pour l’instant si nous avons défini le ViewModel, si nous avons créé un locator pour récupérer l’instance de ce dernier nous ne savons toujours pas comment la vue va s’y rattacher.

Beaucoup de frameworks MVVM propose des solutions de routage basées soit sur un fichier de configuration, soit l’utilisation d’attributs, soit des conventions de noms ou même de l’injection de dépendance.

On peut parfaitement supporter l’une ou l’autre de ces approches mais il faut bien avouer qu’une app mobile n’est tout de même pas (en tout cas pas encore) aussi grosse que certaines applications desktop qui méritent en effet un tel niveau de sophistication. Certaines pratiques vont même jusqu’à instaurer l’utilisation d’interface décrivant le ViewModel pour que les Vues ne voient pas même l’instance de ce dernier, en tout cas qu’au travers de cette liaison faible qu’est l’interface.

Ici notre vue sait très bien à quel ViewModel elle doit se lier. le code-behind de la vue pourrait donc fort bien créer directement une instance du ViewModel et l’attribuer à son contexte. Certains pratiquent de cette façon et ce n’est finalement pas si “coupable” que ça. Dans la réalité je n’ai jamais vu une View dont on change après coup totalement la classe de ViewModel attaché. Une Vue à besoin de certaines données qu’on retrouve dans ses bindings il est donc un peu hypocrite de vouloir séparer ce qui de toute façon est lié.

Mais comme l’expérience nous apprend que des couplages forts finissent toujours par devenir une gêne et un frein à la maintenabilité et l’évolutivité d’un code nous avons recréé le ViewModelLocator. Il offre une séparation suffisante entre consommateur de ViewModels (les Views en général) et les instances des ViewModels eux-mêmes. De fait cette isolation créée par le locator semble assez étanche et faiblement couplée pour éviter de créer d’autres complications. Ainsi c’est dans le code-behind de la page XAML que nous allons instancier le ViewModel mais en passant par le ViewModelLocator pour conserver l’esprit d’un couplage faible.

Le code-behind de la page XAML devient alors :

namespace XFML
{
    public partial class MainView : ContentPage
    {
        public MainView()
        {
            InitializeComponent();
            BindingContext = App.Locator.Main;
        }
    }
}

 

Pas de quoi hurler à la lune, juste une ligne de plus sous “InitializeComponent”. Cette ligne utilise la propriété statique de la classe App permettant de retrouver le ViewModelLocator (donc couplage faible avec ce dernier) pour obtenir enfin le MainViewModel au travers de la propriété Main du locator (second niveau d’indirection et de couplage faible).

Comme on peut le constater les ContentPage de Xamarin.Forms n’utilisent pas la propriété DataContext mais “BindingContext”. C’est une cassure dans la logique XAML dont je ne vois pas l’intérêt ou l’utilité. Mais ce n’est pas très gênant une fois qu’on le sait… C’est donc le BindingContext qui se voit attribuer l’instance du ViewModel.

Voilà, notre page XAML est maintenant connectée à son ViewModel, avec un couplage très faible.

Le code XAML

Nous pouvons reprendre notre MainView pour lui ajouter le visuel de l’application. A savoir un bouton et un label. Nous placerons tout cela dans l’équivalent d’un StackPanel qui sera centré verticalement.

Au final le code XAML spécifique Xamarin.Forms devient le suivant :

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XFML.MainView">
  <StackLayout Orientation="Vertical" VerticalOptions="Center">
    <Button Text="Cliquez ici !" 
            Command="{Binding ClickCommand}"/>
    <Label Text="{Binding FormattedCount}" HorizontalOptions="Center"  />
  </StackLayout>
</ContentPage>

 

Pour qui connait XAML tout cela est limpide. Seules les classes visuelles et leurs options sont un peu différente de WPF. N’oublions pas que c’est à cela que sert Xamarin.Forms : à créer une couche XAML de composants universels qui seront remplacés à la compilation par leurs équivalents NATIFS. Il n’y a pas de surcouche finale, aucune ruse, pas d’hybridation étrange où on planque du HTML pourri dans un webview encapsulé dans une coquille native pour tromper l’ennemi. Rien de tout cela ici. On produit du NATIF. Et par n’importe lequel, on produit du natif PORTABLE.

Tout l’art des Xamarin.Forms c’est d’avoir créer des composants “fantômes” qui permettent d’écrire du XAML universel tout en permettant par une gestion intelligente des dénominateurs communs une compilation native utilisant des contrôles natif pour chaque plateforme.

C’est un magnifique tour de force.

Le code XAML ci-dessus nous montre une structure habituelle, l’utilisation de contrôle ayant des noms évocateurs comme le StackLayout dont on comprend la similarité avec un StackPanel WPF par exemple. On y voit aussi une classe Button pour laquelle nous n’avons pas même besoin de réfléchir au sens ainsi que du binding tout à fait classique sur les propriétés et commandes de notre ViewModel.

Bref une application C#/XAML “comme les autres”.

A cette petite nuance près qu’elle va maintenant tourner sur des plateformes différentes sans réécriture ni même le moindre ajustement.

Etape 3 – Faire tourner !

Pour exécuter notre code il nous faut des émulateurs. Chaque plateforme a les siens. Microsoft utilise Hyper-V pour simuler Windows Phone, Google propose son émulateur, Intel en propose un plus rapide et même Xamarin propose un émulateur Android encore plus rapide. Hélas Microsoft enquiquine tout le monde avec son Hyper-V qui est forcément présent au boot. Impossible de le couper momentanément.

C’est une plaie parce que ce truc est complexe et qu’il gêne les autres émulateurs qui deviennent lents lorsqu’Hyper-V est actif.

J’ai montré dans un post plus ancien comment créer un double boot sur le même Windows 8 avec ou sans Hyper-V pour travailler plus facilement avec ou sans. Je vous ai aussi parlé de HAXM ou de l’émulateur de Xamarin (toujours en bêta). je ne reviendrai pas sur ces questions ici car il est temps de clore ce long article. Mais l’info est dans Dot.Blog ne vous privez pas d’une petite recherche pour mieux cerner ces problèmes s’ils vous sont encore étrangers.

Je n’aborderai même pas le cas de iOS puisque là il faut absolument un Mac en réseau pour faire tourner l’émulateur. Apple à une approche des choses qui me file de l’urticaire rien d’y penser et ce depuis toujours.

Donc munis d’un émulateur Windows Phone qui marche (c’est pas forcément gagné vu tous les pré-requis) et d’un émulateur Android pas trop lent (pas gagné non plus à moins de couper Hyper-V donc ne plus pouvoir travailler sur Windows Phone) nous pouvons envisager de faire tourner notre magnifique application. Il est clair que si on dispose de devices réelles c’est finalement moins compliqué et plus réaliste. Ici les émulateur permettent des captures écrans plus simples.

iOS

Puisque je vous dit que je n’ai pas de Mac en ce moment ! Mais ne vous gênez pour faire l’expérience chez vous si vous êtes un heureux possesseurs de ce type de matériel, comme vous le constaterez ça marche !

Android

Parlons plus sérieusement. Selon l’émulateur que vous choisissez le visuel peut être différent, ici je vous montrerai le Xamarin Android Player et l’émulateur Google.

Xamarin Android Player

Le choix de la device à émuler :

XFML-F-XamPlayer

L’exécution sous Player (n’oubliez pas que toutes les images de l’article sont cliquable pour les afficher en meilleure résolution)  :

XFML-G-RunDroid1

L’exécution sous l’émulateur Google :

XFML-G-RunDroid2

Un petit oubli dans le code XAML fait que le label n’était pas centré… cela est corrigé pour l’exécution suivante sous Windows Phone.

Windows Phone

le joli splash screen Xamarin :

 

XFML-H-RunWP1

 

La même application sans aucun changement, même code, même UI, même C#, même XAML :

 

XFML-H-RunWP2

 

Conclusion

Qu’y-a-t-il de plus à ajouter…

Un code C#, un code XAML, trois plateformes couvertes.

Oui Xamarin n’est pas gratuit, mais pour développer sous Windows Phone Microsoft impose une licence Windows Pro ou Entreprise qui n’est pas cadeau non plus. Et Microsoft impose cela sans aucune bonne justification technique alors que lorsqu’on paye Xamarin on sait pourquoi, la quantité et la qualité du travail derrière tout cela mérite forcément une rémunération. Rappelez-vous : c’est soit trois applications dans trois langages, EDI, visuels et autres totalement différents avec trois compétences différentes ou bien une licence Xamarin et une seule personne qui connait C#/XAML peut faire une app pour les 3 OS du marché…

Forcément l’argument est imparable, tout comme je l’espère ma démonstration Sourire

Bon dev cross-plateforme !

Stay Tuned !

blog comments powered by Disqus