Dot.Blog

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

Gérer du XAML spécifique à chaque plateforme

Le concept du développement multiplateforme est séduisant en théorie : écrire une seule fois le code et le déployer sur plusieurs systèmes à partir d'une base de code unifiée. Cependant, la pratique révèle des complexités, notamment parce que chaque plateforme dispose de ses propres capacités et API qui nécessitent souvent une gestion spécifique.

Les différences entre les plateformes ne se limitent pas aux fonctionnalités ; elles s'étendent également à l'apparence de l'interface utilisateur. Vous pourriez, par exemple, souhaiter adapter les marges en haut de page ou utiliser des icônes distinctes selon la plateforme. Heureusement depuis la création des Xamarin.Forms puis de MAUI tout cela se gère généralement assez facilement et plusieurs méthodes permettent de personnaliser l'interface, y compris la modification des contrôles via des gestionnaires et des extensions de balisage.

Toutefois, il existe des cas exceptionnels où une personnalisation fine au sein d'un même fichier XAML ne suffit pas. Que faire lorsque vous avez besoin que des sections entières d'une page diffèrent significativement selon la plateforme ou le type d'appareil ? La réponse est claire : c'est tout à fait réalisable avec MAUI.

Je vais aborder diverses méthodes pour intégrer des fichiers XAML spécifiques à votre application, en fonction de la plateforme ou du type d'appareil. Une des options serait d'utiliser le multi-ciblage, une technique déjà bien établie sous MAUI grâce au projet "type SDK" ou "projet unique". Cependant, il est important de noter que le multi-ciblage en .NET ne supporte pas encore la compilation conditionnelle de fichiers XAML ni leur exclusion en fonction de la plateforme cible. Je vais donc vous présenter des alternatives éprouvées.

Approches en Code-Behind

Étant donné que cet article concerne XAML, commençons par une mise en page simple. Ici, nous avons un VerticalStackLayout et un x:Name est défini afin que nous puissions manipuler son contenu en y accédant via l'identifiant VerticalLayout dans le code-behind.

<Grid RowDefinitions="*,4*">
  <VerticalStackLayout
    Grid.Row="0"
    x:Name="VerticalLayout" />
</Grid>

Décision au Moment de l'Exécution

La manière la plus simple d'afficher des vues spécifiques à une plateforme est de faire les appels appropriés dans le code-behind en fonction de la plateforme d'exécution actuelle :

if (DeviceInfo.Platform == DevicePlatform.Android)
{
    VerticalLayout.Add(new Android.ViewAndroid());
}
else if (DeviceInfo.Platform == DevicePlatform.iOS)
{
    VerticalLayout.Add(new iOS.ViewiOS());
}

Inconvénient de cette Approche

L'inconvénient de cette méthode est que le code résultant sera disponible sur toutes les plateformes cibles. Cela signifie que même si une vue est spécifique à Android, le code pour cette vue sera toujours inclus dans le package de l'application pour toutes les autres plateformes, ce qui peut entraîner une augmentation de la taille du package et une complexité accrue lors de la maintenance du code. Mais le plus gênant est que la prise de décision se trouve dans le code-behind alors qu'il devrait être dans un ViewModel mais que ce dernier ne peut pas manipuler des classes d'UI. C'est assez embêtant et si un bout de code-behind exemple semble faire le job, en réalité se mettre en conformité avec MVVM compliquera beaucoup les choses.

Compilation Conditionnelle

Une approche alternative et légèrement meilleure par rapport à la décision au moment de l'exécution consiste à utiliser la compilation conditionnelle :

#if ANDROID
    VerticalLayout.Add(new Android.ViewAndroid());
#elif IOS
    VerticalLayout.Add(new iOS.ViewiOS());
#endif

Avantage de cette Approche

L'avantage ici est que seuls les appels appropriés se retrouvent dans le code de l'application compilée pour chaque plateforme cible, éliminant ainsi la nécessité de prendre une décision au moment de l'exécution. Mais nous sommes loin de la perfection.

Approche Uniquement en XAML

Il est également possible d'afficher uniquement les parties pertinentes de l'interface utilisateur en fonction de la plateforme d'exécution actuelle en utilisant uniquement du XAML, sans code C#, en tirant parti de <OnPlatform>, c'est ma méthode préférée (les problématiques d'UI restent côté UI) :

<Grid RowDefinitions="*,4*">
  <ContentView
    Grid.Row="1"
    Padding="20">
    <OnPlatform x:TypeArguments="View">
      <On Platform="Android">
        <android:ImageViewAndroid />
      </On>
      <On Platform="iOS">
        <iOs:ImageViewiOS />
      </On>
    </OnPlatform>
  </ContentView>
</Grid>

Ici, nous avons un ContentView qui sert de conteneur et nous contrôlons son contenu en utilisant la classe <OnPlatform> et en fournissant différentes vues pour chaque plateforme. Il est important d'inclure l'attribut x:TypeArguments="View" car nous devons indiquer à <OnPlatform> quel est le type de retour, étant donné que la classe ContentView n'accepte que des instances de View comme contenu. C'est vraiment aussi simple que cela !

A noter
Ceci est équivalent à l'approche de décision au moment de l'exécution dans le code-behind, ce qui signifie que toutes les vues de toutes les plateformes seront incluses dans le bundle de l'application. L'utilisation de <OnPlatform> avec des vues secondaires insérées évite le code conditionnel (toujours difficile à tracer et à lire) et même le code C# tout court, laissant à l'UI et son langage se charger de l'UI.

Il est également possible d'utiliser <OnIdiom> pour fournir différentes vues en fonction du type d'appareil (par exemple, tablette, téléphone, ordinateur de bureau) :

<Grid RowDefinitions="*,4*">
  <ContentView
    Grid.Row="1"
    Padding="20">
    <OnIdiom x:TypeArguments="View">
      <On Idiom="Tablet">
        <tablet:TabletView />
      </On>
      <On Idiom="Phone">
        <phone:PhoneView />
      </On>
    </OnIdiom>
  </ContentView>
</Grid>

Imperfections à considérer

Gardez à l'esprit qu'en raison de la nature de la mise en œuvre des approches ci-dessus et du fait que le multi-ciblage n'est actuellement pas pris en charge pour le XAML, seule la partie en code-behind est soumise à la compilation conditionnelle. Cela signifie que tout le code XAML de votre projet d'application sera expédié à toutes les plateformes cibles, y compris le XAML inutilisé qui est spécifique à d'autres plateformes cibles (ou types d'appareils). 

Conclusion

Comme nous l'avons vu, il est en réalité assez facile d'avoir du XAML spécifique à chaque plateforme (ou appareil) dans les applications MAUI en utilisant des décisions au moment de l'exécution (soit en C# soit en XAML) ou la compilation conditionnelle.

Bien qu'il ne soit pas possible (encore) d'utiliser la compilation conditionnelle pour XAML ou même le multi-ciblage basé sur le nom de fichier et de dossier pour les fichiers XAML, les approches présentées peuvent vous aider à améliorer l'expérience utilisateur de votre application multiplateforme en fournissant des vues spécifiques à chaque plateforme ou type de device avec facilité. 

Il faut rappeler malgré tout que si la personnalisation d'une marge, d'un contrôle, au sein d'une page XAML est assez fréquent, l'utilisation de pages totalement spécifiques à chaque plateforme est assez rare. Si jamais il en est autrement pour une App donnée et qu'une majorité de pages est concernée, alors autant concevoir autant d'Apps que de plateformes, même avec MAUI et en utilisant une base de code commune (DLL MAUI par exemple).

Un jour peut-être Microsoft implémentera le multi-ciblage pour les fichiers XAML, cela sera certainement une bonne nouvelle, au moins pour la cohérence de MAUI, mais avouons-le, cela ne sera pas vécu comme une révolution que tout le monde attendait. Si cela n'a pas été déjà fait on tient peut-être là la raison…

Stay Tuned !

Le Guide Complet de.NET MAUI ! Lien direct Amazon : https://amzn.eu/d/95wBULD

Près de 500 pages dédiées à l'univers .NET MAUI !

Existe aussi en version Kindle à prix réduit !

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