Dot.Blog

C#, XAML, WinUI, WPF, Android, MAUI, IoT, IA, ChatGPT, Prompt Engineering

Xamarin.Forms Partie 2 XAML devient Cross-Plateforme!

[new:31/08/2014]Dans l’introduction publiée hier je vous ai montré la simplicité et la puissance des Xamarin.Forms, mais il y a encore plus fort : elles peuvent être définies en XAML !

XAML ?

On ne présente plus XAML, langage de description graphique absolument merveilleux et vectoriel s’adaptant à toutes les résolutions et fonctionnant sur tous les PC depuis XP jusqu’à Windows 8.1 en passant par Windows Phone et WinRT. Pendant un temps, celui de Silverlight, ce langage est même devenu universel, fonctionnant sur toutes les plateformes.

XAML suit un formalisme XML pour décrire un objet visuel, qu’il s’agisse d’une page WinRT, d’un User Control Silverlight ou WPF, ou d’un template d’affichage de données sous Windows Phone. Dans tous ces cas de figure XAML est le même, vectoriel, avec il est vrai quelques nuances de ci de là. Par exemple le XAML de WPF supporte la 3D, c’est le seul. Silverlight pour le Web ou celui utilisé aujourd’hui pour développer sous Windows Phone n’est qu’un sous ensemble de WPF, par exemple la coercition de valeurs pour le propriétés de dépendance n’existe pas. Bref XAML est à la fois un et multiple mais quand on sait s’en servir on se retrouve toujours en terrain connu peu importe l’implémentation.

XAML c’est aussi un éditeur visuel surpuissant, Blend, anciennement Expression Blend. Depuis quelques temps le designer visuel de Visual Studio est presque du même niveau. Presque seulement. Données de test, conception de templates, etc, tout cela réclame Blend pour le faire visuellement.

Alors de quel XAML parlons nous dans Xamarin.Forms sachant que ces dernières ne sont qu’une façade qui est compilée en natif sur chaque plateforme ? Etant donné que ni Android ni iOS n’offrent de plateforme vectorielle, le XAML dont nous parlons pourrait sembler très différents de celui auquel nous avons affaire habituellement.

En réalité le XAML des Xamarin.Forms est un sous-ensemble de XAML, comme celui de Silverlight ou Windows Phone. Ni plus ni moins. Le code s’exprime de la même façon en utilisant un jeu de contrôles particulier comme on utiliserait une librairie de chez Telerik par exemple. Bien entendu ce sous-ensemble de XAML ne possède pas les primitives graphiques qui obligerait la présente d’un moteur vectoriel de rendu. Si on doit placer de l’imagerie on utilisera des PNG par exemple plutôt que des User Control construits graphiquement en courbes de Béziers. C’est là qu’est tout la différence.

Car pour le reste le XAML des Xamarin.Forms est en tout point semblable au XAML traditionnel, il en est vraiment très proche. Il ne lui manque que la parole. Enfin la conception visuel je veux dire… Car en effet pour l’instant c’est un XAML qu’il faut écrire à la main.

Même si rien n’a été annoncé en ce sens par Xamarin je suppose qu’un jour prochain le support d’un designer visuel sera proposé. Cette excellente idée ne peut s’arrêter là. Ce n’est que la première version et la proximité du XAML Xamarin et de celui de Microsoft est tel que tout le monde pense tout de suite à utiliser VS ou Blend. Xamarin y pense déjà certainement aussi. D’autant que concevoir des designer visuels n’est pas une nouveauté pour eux qui ont créé ceux pour Android et iOS dans Xamarin.Studio et en plugin pour VS ! Si on fait abstraction du vectoriel justement et de tout ce que cela implique, le XAML de Xamarin.Forms n’est pas très différent du XML de description Android. Or Xamarin possède déjà un designer pour ce dernier… L’avenir pourrait être passionnant… En attendant le XAML Xamarin s’écrit à la main…

UI en XAML ou C# ? 

Xamarin.Forms offre les deux approches donc. On peut comme je l’ai fait voir dans l’article précédent décrire les pages totalement en C#, c’est simple, compréhensible et on n’utilise qu’une seule compétence. Ce mode peut s’avérer parfait pour ceux qui ne se sentent pas très à l’aise avec XAML. Et puis on peut aussi, comme nous allons le voir aujourd’hui, décrire les pages en XAML.

Sachant que pour l’instant en tout cas il n’y a pas de designer visuel pour le XAML de Xamarin.Forms on peut se demander quel est l’intérêt d’écrire les UI en XAML plutôt qu’en C#.

La question est légitime. Une partie de la réponse a été donnée plus haut : pour ceux qui ne sont pas à l’aise avec XAML faire tout en C# est une option parfaitement valable. En revanche pour ceux qui connaissent XAML ils trouveront là un moyen plus moderne de travailler. Car XAML est un langage descriptif à la différence de C# qui est un langage impératif… J’ai abordé récemment dans les articles sur F# la différence d’approche entre langage fonctionnel et langage impératif ou orienté objet. On retrouve les mêmes nuances avec les langages descriptifs comme XAML comparés aux autres langages.

En XAML on décrit le visuel. On explique uniquement ce qu’on veut voir. 100% du code sert à définir l’objet visuel traité.

En C# 80% du code est mécanique et consiste à appeler des constructeurs, à passer des paramètres, à créer des listes, des objets qu’on imbrique dans d’autres. Bref à faire de la plomberie POO. 20% seulement consiste à donner une valeur à une couleur, à fixer numériquement un padding, etc.

Comme je le disais les deux approches peuvent sembler similaires car toutes les deux pour l’instant se codent à la main.

Mais entre du C# et du XAML il existe une différence essentielle, comme entre du C# et du F#.

Maintenir, c’est à dire en correctif tant qu’en évolutif, du XAML pour des objets visuels est plus intéressant que de maintenir un équivalent en C#, c’est tout simplement mieux adapté.

Alors que choisir ?

C’est bien simple, tant qu’un éditeur visuel n’existe pas pour le XAML Xamarin, ce qui marquerait un avantage décisif pour ce dernier, il y autant d’avantages à choisir XAML que C# pour décrire les UI. A vous d’utiliser l’approche qui tout bêtement vous semblera la plus agréable, la mieux adaptée à votre besoin… Je vais même aller plus loin : mixez les deux approches dans vos applications en fonction de la nature des pages ! En effet des pages très visuelles avec des data template ou autre gagneront en lisibilité et en maintenabilité à être écrites en XAML. En revanche les pages dynamiques avec du contenu qui s’adapte aux données sera du coup bien plus simple à maintenir et à créer en utilisant directement C# !

Xamarin.Forms est un framework solide car il permet tous les “décrochages” et le mélange des approches sans mettre en péril tout l’édifice et se retrouver coincé. Une page est plus simple en C#, codez là en C#, une page est plus simple en XAML, utilisez ce dernier, une page est plus facile à coder en natif et bien codez là en pur natif, etc… On peut ainsi jongler dans une même application entre des approches de l’UI totalement différentes mais convergentes grâce à l’intelligence de l’outil.

Côte à côte

Comment mieux sentir la différence des approches XAML et C# pour coder les UI qu’en les plaçant côte à côte ?

C’est ce que je vous propose ici en reprenant l’exemple présenté dans l’article précédent et en remplaçant toutes les fiches codées en C# par des fiches identiques codées en XAML !

En partant d’une copie du projet d’hier (un vrai copier / coller de tout le répertoire de la solution) nous allons voir comment chaque fiche, chaque évènement, chaque binding, chaque template peut s’exprimer à l’identique en XAML.

Attention : dans cette manipulation je vais conserver une vraie copie, donc les mêmes noms de sous-répertoire, de fichiers, même les namespaces, les classes, tout cela sera identique. Seul le répertoire de plus haut niveau sera renommé avec un suffixe “XAML”. Il est donc essentiel de ne pas se mélanger les pinceaux électroniques pour éviter d’effacer ou de modifier des fichiers dans la “mauvaise” solution dans le cas où vous auriez sur votre disque le projet d’hier et celui d’aujourd’hui !

La fiche principale (liste)

Dans sa version originale il s’agissait du fichier de code “BirdsPage.cs”. Une classe de type ContentPage était créée avec une liste et son template plus le code de navigation sur la sélection d’un oiseau.

Dans sa version XAML, après avoir supprimé “BirdsPage.cs” (je pars vraiment d’un copier/coller de la solution d’hier), je rajoute dans le même sous-répertoire “Form” une nouvel item. Dans la liste que Visual Studio ouvre on trouve “Form Xaml Page” :

image

Je vais donc créer une nouvelle Form de Xamarin.Forms que je vais appeler de la même façon, “BirdsPage”. Comme pour tout code XAML Visual Studio créée en réalité deux fichiers : “BirdsPage.xaml.cs” le code-behind, et “BirdsPage.xaml” le code XAML.

Bien entendu comme il n’y a pas de designer pour ce type de XAML on obtient immédiatement une exception dans le designer de VS qui s’ouvre, il suffit de le ferme et de ne garder que l’éditeur de code. Ce n’est pas très propre mais pas gênant puisque nous le savons, je l’ai dit plusieurs fois, ce XAML là ne se pratique pour l’instant que par code “à la main” et non pas visuellement.

Le contenu XAML qui vient d’être créé est 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="DotBlogTest.Form.BirdsPage">
	<Label Text="{Binding MainText}" VerticalOptions="Center" HorizontalOptions="Center" />
</ContentPage>

 

Il s’agit bien d’une ContentPage, comme celle de la version C#. Ce n’est que coïncidence Xamarin.Forms est très fort mais il ne lit pas encore les pensées du développeur. Cette page est presque vide puisqu’elle contient un simple Label dont la propriété texte est bindée à une propriété MainText qui proviendrait du ViewModel associé. Cela nous indique clairement que ces Forms XAML de Xamarin supportent le data binding ! C’est ce qui était merveilleux avec MvvmCross qui ajoutait le databinding dans les projets natifs. Ici c’est “out of the box” ou presque et cela fonctionne partout avec une seule et même syntaxe… Comme je le répète peut-être comme une supplique, il ne manque que le designer visuel pour en faire un XAML universel (j’en tremble rien que d’y penser ! Pour rire).

Bref tout cela est bien sympathique mais nous avons une fiche principale à coder…

En quelques secondes il est facile pour qui connait XAML de taper le code qui suit :

<?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="DotBlogTest.Form.BirdsPage" Title=”oiseaux”>
	<ListView x:Name="List" ItemSource="{Binding Birds}" ItempTapped="OnItemsSelected">
        <ListView.ItemTemplate>
            <DataTemplate>
                <TextCell Text="{Binding Name}" Detail="{Binding Description}"/>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</ContentPage>

 

Le Label de démonstration a été supprimé, à sa place j’ai placé un ListView qui possède un ItemTemplate utilisant un TextCell dont les propriétés sont bindées au nom et à la description de l’oiseau sélectionné. Bref exactement la même chose que le code C# original que je vous redonne ci-dessous pour comparaison :

public BirdsPage ()
{
	Title = "Oiseaux"; 
	var list = new ListView {ItemsSource = new BirdsViewModel().Birds};
	var cell = new DataTemplate (typeof(TextCell));
	cell.SetBinding (TextCell.TextProperty, "Name");
	cell.SetBinding (TextCell.DetailProperty, "Description");
	list.ItemTemplate = cell;
	list.ItemTapped += (sender, args) =>
	   {
	       var bird = args.Item as Bird;
	       if (bird == null) return;
	       Navigation.PushAsync(new DetailPage(bird));
	       list.SelectedItem = null;
           };
        Content = list;
}

 

C’est bien ce que je vous disais, en mode C# on écrit beaucoup de plomberie (créations diverses, binding, etc) alors qu’en XAML on reste purement déclaratif ce qui est bien plus adapté à la description d’une vue.

Toutefois on triche un peu car il manque des petits bouts au code XAML pour faire exactement la même chose. Notamment la gestion du clic que nous avons bindée à un OnItemSelected qui n’existe nulle part pour l’instant …

Où placer ce code appartenant à la fiche ? Le gestionnaire de clic devrait être bindé à une commande du ViewModel mais pour l’instant je ne veux pas modifier toute l’application ce qui embrouillerait les choses. Nous ferons fi ici de l’orthodoxie MVVM… Je placerai donc ce code avec l’initialisation de la classe c’est à dire dans le code-behind. Le jeu consiste à remplacer “à l’arrache” les Forms C# par des Forms en XAML. On voit d’ailleurs que l’approche XAML aide à soulever les bonnes questions.. Dans le code C# nous avions codé l’évènement comme une Lambda dans la création de l’UI ce qui ne choquait personne… sauf les plus tatillons. Mais ici en utilisant un outil de description visuelle, XAML, nous obligeons à la réflexion sur la place du code non visuel. Et même si pour cet exemple je ne le déplace pas dans le ViewModel c’est bien entendu là qu’il devrait se trouver ! Simuler à iso fonctionnalités est notre seul but dans cet article. Et c’est ce que nous faisons. En C# l’UI traitait l’évènement, en XAML aussi. Cela soulève d’ailleurs une autre question : le navigationnel doit-il être considéré comme un code “normal” ou bien comme faisant partie de l’UI. Si nous avions un XAML complet, nous serions tenté de placer un Behavior pour gérer la navigation. Donc côté UI… Mais tout cela est hors de mon propos du jour, juste quelques éléments pour faire réfléchir Clignement d'œil

Voici donc le code-behind de la Form :

using DotBlogTest.Model;
using DotBlogTest.ViewModel;
using Xamarin.Forms;

namespace DotBlogTest.Form
{
	public partial class BirdsPage : ContentPage
	{
		public BirdsPage ()
		{
		    InitializeComponent ();
		    BindingContext = new BirdsViewModel();
		}

	    public void OnItemsSelected(object sender, ItemTappedEventArgs args)
	    {
	        var bird = args.Item as Bird;
                if (bird==null) return;
	        Navigation.PushAsync(new DetailPage(bird));
	        var listView = sender as ListView;
	        if (listView != null) listView.SelectedItem = null;
	    }
	}
}

 

Le constructeur appelle l’initialisation des composants, ce qui est classique pour du XAML, puis nous créons l’instance du ViewModel pour initialiser la propriété BindingContext. Ici aussi il y aurait beaucoup de choses à dire, notamment sur la création systématique du VM alors que nous pourrions utiliser un cache ou mieux un conteneur d’IOC. Mais encore une fois ce sont des sujets que j’ai traités maintes fois, ici ce n’est pas le propos. Restons simples et agiles. Après tout beaucoup de petites apps mobiles pour entreprise ne réclament pas plus de complexité. La beauté du geste à un coût qui n’est pas toujours justifié.

On note donc la présence du gestionnaire de sélection d’item de la ListView avec un code quasiment identique à celui de l’expression Lambda de la version C#. Le code est ici tapé dans Visual Studio avec Resharper qui conseille parfois quelques aménagements. J’aime suivre ces conseils qui sont presque toujours justifiés.

Mixed !

A cette étape particulière du projet nous avons fait la moitié du chemin. Deux fiches, l’une refaite totalement en XAML, l’autre encore dans son état original en C#.

Rien d’autre que ce qui a été montré plus haut n’a été touché, ni dans le code partagé et encore moins dans le projet hôte Android. Rien.

Ayant pris soin de réutiliser le même nom de classe pour la page principale, l’application devrait fonctionner non ?

Après deux ou trois corrections mineures (j’avais oublié le titre de la page, dans le code-behind il manquait un “s” dans le nom du gestionnaire d’évènement… bref la routine !) je lance le projet et voici ce que nous voyons :

image

 

La même liste d’oiseaux, présentée de la même façon. J’ai juste modifié le titre pour signifier que l’application est différente en réalité.

Et si on clic sur un item ? Et bien ça marche et cela retourne exactement la même page que dans la version précédente. A la fois parce qu’elle n’a pas été changée et que même si elle l’avait été on ne pourrait pas voir de différence…

Conclusion

Le tricheur il ne va pas jusqu’au bout ! Tsss … Mais c’est pour mieux vous laissez le plaisir de le faire ! Je vous offre gracieusement et sans supplément le code de l’application, c’est fait pour ça !

Du point de vue de cet article refaire la même chose avec la fiche de détail n’aurait de toute façon qu’un intérêt très réduit.

Plus loin, vous montrer que l’application fonctionne dans ce mode mixte, une fiche en XAML l’autre en C#, est même un plus. La souplesse des Xamarin.Forms que j’ai déjà évoquée est bien là, on fait ce qu’on veut et ça marche quand même. On choisit son approche, globale ou au coup par coup selon le besoin, et ça marche. On code en XAML, ça marche. On fait du databinding, ça marche et c’est portable cross-plateforme…

C’est vrai que les Xamarin.Forms, techniquement et à défaut d’un designer visuel (j’y tiens!) ce n’est qu’une simple librairie de code comme beaucoup d’entre nous auraient pu en écrire. Rien de vraiment compliqué. Au premier coup d’œil seulement…

Car derrière chaque classe utilisée se trouve un mécanisme qui traduit tout cela en composants natifs à la compilation. C’est un système totalement cross-plateforme, code mais aussi UI qui s’utilise de façon transparente, c’est un boulot énorme !

C’est vraiment beau. Du cross-plateforme “out of the box” avec C# et XAML…

Allez Miguel, un petit effort, offre nous un designer visuel pour les Xamarin.Forms et je te bénirai sur cent milles générations (en toute amitié je te fais déjà un gros bisous c’est bien mérité Tire la langue) !

Les Xamarin.Forms avec XAML c’est le pied de nez d’outre tombe de Silverlight à Sinofsky ce visionnaire de pacotille… C’est une fois encore la preuve qu’on n’est pas prêt de détrôner le couple C# / XAML pour faire rapidement des applications professionnelles pour toutes les plateformes. C’était la définition de Silverlight non ?

Merci Miguel, merci de la part de Silverlight.

C’est pas fini, restez encore : voici le code de la solution

et… Stay Tuned !

blog comments powered by Disqus