Dot.Blog

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

Xamarin.Forms : OnPlatform (et les styles)

La méthode helper OnPlatform est souvent sous-utilisée, peut-être parce qu'elle a évolué dans le temps et que cela a créé une confusion. Levons le doute !

OnPlatform

C’est peut-être l’une des méthodes les plus emblématiques des Xamarin.Forms, celle qui cristallise ce qu’est ce framework : un outil cross-plateforme !

Et dans un code commun écrit une seule fois comment tenir compte à l’exécution des spécificités d’une plateforme ou d’une autre ? C’est un peu paradoxal. Un seul code ou pas un seul code ?

Xamarin.Forms règle la question avec notamment OnPlatform.

On peut l’utiliser dans le code C# mais aussi en XAML. Et grâce à cet helper il est possible de fixer des valeurs spécifiques à une plateforme tout en restant dans un code commun. Magique et pratique !

Ancienne et nouvelle version

Il y a donc eu des changements dans OnPlatform. Ce changement est intervenu il y a longtemps (2017) mais la confusion qu'il a entraîné notamment par la présence de tutors pas forcément à jour sur le Web se ressent encore aujourd'hui par une sous-utilisation.

Pourquoi ? Parce que Xamarin.Forms prend en compte les changements qui s’opèrent chez Microsoft en permanence…On sait que Xamarin.Forms va basculer vers MAUI en novembre prochain (2021), et pas mal de choses ont été ajoutées dans la dernière et l'avant-dernière grande release pour préparer le terrain.

Ce fut la même chose en 2017 car au départ Xamarin.Forms hérite des outils Xamarin, c’est à dire le support de Android et iOS uniquement. Avec le rachat par Microsoft le support un peu forcé de Windows Phone a forcé à des adaptations de OnPlatform  pour prendre en compte cette troisième cible (sans trop de mal).

Mais tout le monde le sait (ou l'a oublié !), Microsoft a abandonné Windows Phone depuis 2018. 

C’est dommage pour Windows Phone. Et c’est dommage pour ceux qui fantasmaient sur Surface Phone. Il est d'ailleurs revenu timidement il y a peu mais ... sous Android ! Il y avait pourtant de la place pour Windows Phone, c’était un bon OS mobile. Mais d’autres aussi sont passés à la trappe. Qu’importe, notre job c’est prendre le marché tel qu’il est, pas comme on voudrait qu’il soit. Si les développeurs eux-mêmes n’étaient pas pragmatiques et logiques qui le serait sur terre hein ? !

Mais en contrepartie Microsoft s’est investi beaucoup plus dans le cross-plateforme et comme je soutiens cette démarche depuis des années, moi ça me fait plaisir ! Pas tant parce que cela me donne raison, ça fait toujours plaisir il ne faut pas mentir, mais surtout parce que le cross-plateforme était forcément l’avenir et que j’y croyais (Mes premiers papiers sur le développement cross-plateforme et mes premières vidéos sur MvvmCross ont aujourd'hui plus de 8 ans...).

Dans cette voie Xamarin a donc rapidement été adapté pour supporter UWP ce qui lui ouvre au passage le monde Windows, mais Windows Phone n’était pas encore totalement mort. Un paramètre de plus donc…

Puis MacOS a est visé et pris en compte. Encore des paramètres à ajouter à la méthode helper...

Puis Windows Phone a fini par mourir, il a fallu l’enlever et ajouter une clé plus distinctive pour les apps UWP non ?

Voilà pourquoi OnPlatform a changé, cette petite méthode helper est révélatrice de toutes les errances de Microsoft avec par force ses mauvaises conséquences mais aussi ses bonnes nouvelles !

De fait plutôt que de conserver une liste de propriétés bien établie (iOS, Android, WinPhone) pour les variantes, OnPlatform s’est orienté vers un support plus découplé. Une propriété apparaît, RuntimePlatform qui retourne… une string. Les tests se font désormais avec un switch en testant des chaînes. Ils sont tranquilles, avec ce procédé la classe n’aura plus à être bricolée.

Les chaînes c’est un peu dangereux malgré tout, je n’aime pas ça dans une appli. Ils y ont pensé et la classe Device (qui supporte RuntimePlatform) possède aussi des constantes au nom des OS supportés. Device.iOS, Device.Android, etc. Cela évite d’utiliser des chaînes dans son application.

Reste qu’en XAML les constantes de Device sont moins accessibles et qu’on retrouve des strings ce qui n’est pas le top. Prism par exemple ajoute une Enum qui contourne le problème pour le code C#, mais pas pour XAML.

Ancienne version mais qu'on retrouve sur le web

Pour rappel uniquement, on écrivait un code de ce genre par exemple :

<!-- Vieille version de OnPlatform XAML -->
<StackLayout>
  <StackLayout.Padding>
    <OnPlatform x:TypeArguments="Thickness" 
                Android="0, 0, 0, 0" 
                WinPhone="0, 0, 0, 0" 
                iOS="0, 20, 0, 0"/>
  </StackLayout.Padding>
</StackLayout>

La bonne version

<!-- Nouvelle version XAML -->
<StackLayout>
  <StackLayout.Padding>
   <OnPlatform x:TypeArguments="Thickness">
     <On Platform="Android" Value="0, 0, 0, 0"/>
     <On Platform="WinPhone" Value="0, 0, 0, 0"/>
     <On Platform="iOS" Value="0, 20, 0, 0"/>
    </OnPlatform>
  </StackLayout.Padding>
</StackLayout>

<!-- Nouvelle version améliorée -->
<StackLayout>
  <StackLayout.Padding>
   <OnPlatform x:TypeArguments="Thickness">
     <On Platform="Android, WinPhone">0</On>
     <On Platform="iOS">0,20,0,0</On>
    </OnPlatform>
  </StackLayout.Padding>
</StackLayout>

En C# cela donne :

switch(Device.RuntimePlatform)
{
    case Device.iOS:
        MainPage.BackgroundColor = Color.Black;
        break;
    case Device.Android:
        MainPage.BackgroundColor = Color.Red;
        break;
    case Device.WinPhone:
        MainPage.BackgroundColor = Color.Orange;
        break;
    default:
        MainPage.BackgroundColor = Color.Transparent;
        break;
}

Définir des styles avec OnPlatform

Avec OnPlatform on peut à tout moment dans son code C# ou XAML faire intervenir des petites variations selon la plateforme en cours d’exécution, c’est vraiment pratique. Mais ce n’est pas forcément une “bonne pratique”  !

En XAML il est bien préférable de définir des styles centralisés qu’il sera facile de maintenir. De plus on s’assure d’avoir un look & feel homogène dans toute l’appli.

Par exemple on peut utiliser un style implicite pour le padding des pages sachant qu’il ne faut pas oublier de mettre 20 pixels en haut sous iOS mais que cela n’a pas d’intérêt ailleurs. Au lieu de le faire dans chaque page autant le généraliser par un style implicite dans App.Xaml.

Un style implicite pour rappel est un style qui n’a pas de “Key”, tout le reste est “normal”. Sans clé d’accès un style est considéré s’appliquant par défaut à tous les objets du type ciblé.

En s’aidant de OnPlatform on peut donc régler ce problème une fois pour toute dans son application en ajoutant un style implicite dans App.Xaml :

<?xml version="1.0" encoding="utf-8" ?>
<prism:PrismApplication xmlns="http://xamarin.com/schemas/2014/forms"
                         xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                         xmlns:prism="clr-namespace:Prism.DryIoc;assembly=Prism.DryIoc.Forms"
                         x:Class="Demo.App">
  
   <Application.Resources>
     <ResourceDictionary>
       <OnPlatform x:Key="PagePaddingStyle" x:TypeArguments="Thickness">
         <On Platform="Android, WinPhone, Windows" Value="10,5,10,5"/>
         <On Platform="iOS" Value="0,20,0,0"/>
       </OnPlatform>
      
       <Style TargetType="NavigationPage">
         <Setter Property="Padding" Value="{StaticResource PagePaddingStyle}"/>
       </Style>
     </ResourceDictionary>
   </Application.Resources>

</prism:PrismApplication>


Dans cet exemple j’ai mis une valeur de 10 pixels à gauche et à droite et 5 pixels en haut et en bas pour tous les OS en utilisant la possibilité de les lister avec une virgule (au lieu d’ouvrir une nouvelle balise “On”) et j’ai indiqué la marge de 20 pixels haute pour iOS.

Cette valeur pour un Thickness est définie en constante dans le dictionnaire de ressource (avec une clé), cela permettra de l’utiliser plusieurs fois sans la répéter. Utilisez toujours cette approche pour obtenir un code XAML modulable et facilement maintenable. Regroupez au même endroit les constantes de couleur, de padding, etc. Définissez ensuite les styles en utilisant ces constantes.

Ensuite je définie un style sans clé (Key) ciblant NavigationPage (car c’est la première de ce programme de démo). On pourrait cibler ContentPage tout dépend de l’App et si on utilise Prism ou un autre toolkit MVVM.

Conclusion

Révélateur des changements importants de philosophie chez Microsoft le OnPlatform de Xamarin.Forms s’avère aussi être un outil symbolisant à lui-seul le caractère cross-plateforme des XF.

Mais c’est avant tout un helper bien pratique et sa modification pour supporter toujours plus de plateformes a été une étape essentielle. A noter, l’ancienne syntaxe a été marquée “obsolète”, donc vous verrez dans votre code XAML et C# des avertissements. Pour les corriger utilisez la nouvelle syntaxe ! Double attention : Lors du passage à MAUI il n'y a aura plus d'avertissement, cela ne marchera pas tout simplement (le changement étant jugé assez ancien désormais).

Stay Tuned !

Faites des heureux, partagez l'article !
blog comments powered by Disqus