Dot.Blog

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

Xamarin.Forms et le nouveau OnPlatform (et les styles)

Dans la version 6.2+ de Xamarin.Forms un changement a été fait dans la méthode helper OnPlatform…

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.

Pourquoi ? Parce que Xamarin.Forms prend en compte les changements qui s’opèrent chez Microsoft…

Au départ Xamarin.Forms hérite des outils Xamarin, c’est à dire le support de Android et iOS. Avec le rachat par Microsoft qui se pressentait est arrivé le support un peu forcé de Windows Phone déjà déclinant. OnPlatform s’est donc adapté à cette troisième cible sans trop de mal.

Mais tout le monde le sait, on ne lit que ça sur le Web, Microsoft a supprimé la ligne budgétaire de Windows Phone pour l’année à venir. Déjà à peine maintenu, maintenant sans budget ni plus aucune nouvelle machine en vente, c’est donc un produit mort définitivement. On le savait depuis longtemps et on savait que MS n’allait pas manger son chapeau dans une annonce claire… Il faut faire ça dans la lenteur histoire que ça passe mieux. Les dernières mauvaises annonces arrivant au cœur de l’été, MS nous la joue très gouvernemental donc…

C’est dommage pour Windows Phone. Et c’est dommage pour ceux qui fantasmaient sur Surface Phone. Il y avait 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’investit beaucoup dans le cross-plateforme et comme je soutiens cette démarche depuis 4 ans au moins et bien avant ce retournement de MS et le rachat de Xamarin, 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.

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 est visé, il faudra ajouter certainement de quoi le prendre en compte…

Et Windows Phone meurt, il faut l’enlever et ajouter une clé plus distinctive pour les apps UWP non ?

Voilà pourquoi OnPlatform change, 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 a ê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

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>

Nouvelle 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.

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 est une bonne chose. A noter, l’ancienne syntaxe est marquée “obsolète”, donc vous verrez dans votre code XAML et C# des avertissements. Pour les corriger utilisez la nouvelle syntaxe !

Stay Tuned !

blog comments powered by Disqus