Dot.Blog

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

Xamarin.Forms : La fusion automatique des Dictionnaires XAML

Utiles pour stocker des styles et autres ressources, les dictionnaires XAML sous Xamarin.Forms peuvent aussi être fusionnés automatiquement (et non manuellement). Mais pourquoi vouloir le faire et comment ?

imageResourceDictionnary

Le placement de styles et d’autres ressources dans un ResourceDictionary est un excellent moyen de réutiliser ces éléments au sein de l’application ou d’une page selon le niveau où il apparait. Pendant longtemps vous ne pouviez fusionner qu'avec un dictionnaire de ressources externe, à l'aide précédemment de MergedWith et avec Source aujourdhui. Avec MergedDictionaries vous pouvez séparer vos styles en plusieurs fichiers et les fusionner comme vous le souhaitez sur chaque page (ou au niveau de App.Xaml ce qui est le mieux malgré tout).


Comment les utiliser ?

Les dictionnaires peuvent être définis dans des fichiers à part, c’est ce qui fait tout l’intérêt de la chose d’ailleurs et qui mène à vouloir les fusionner ! Un fichier peut définir les couleurs utilisées dans l’application (et rien d’autres), un autre certains styles généraux, puis d’autres dictionnaires peuvent définir des ressources, etc.

Prenons un exemple et créons un nouveau fichier XAML puis pour créons le dictionnaire de ressources comme ci-dessous. Ici le nom du nouveau fichier est arbitrairement (et avec une créativité audacieuse) MyResourceDictionary.

<?xml version="1.0" encoding="utf-8" ?>
<ResourceDictionary xmlns="http://xamarin.com/schemas/2014/forms"
                    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                    x:Class="Mobile.Style.MyResourceDictionary"> 
    <Style TargetType="Button"> 
        <Setter Property="TextColor" Value="White" /> 
    </Style>
</ResourceDictionary>

Le dictionnaire ne définit qu’un Style qui ne porte pas de nom, donc s’appliquant par défaut à la classe cible (Button), et ce style est fort modeste puisqu’il se borne à fixer la couleur blanche au texte. Le fichier XAML (et la classe qu’il représente) a été défini dans l’espace de nom fictif Mobile.Style.

Pour utiliser ce style dans une page Xamarin.Forms :

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:theme="clr-namespace:Mobile.Style"
             x:Class="Mobile.MainPage">
    <ContentPage.Resources>
        <ResourceDictionary.MergedDictionaries>
            <theme:MyResourceDictionary />
            <!—et tous les autres qui pourraient servir -->
        </ResourceDictionary.MergedDictionaries>
    </ContentPage.Resources>
</ContentPage>

Comme on s’y attend il faut référencer l’espace de nom et lui donner un alias XAML qui est ici “theme”.

On comment par ouvrir la propriété Resources de la ContentPage comme on le fait habituellement, puis c’est là que les choses changent puisqu’on ouvre une balise MergedDictionaries, dictionnaires fusionnés. Et à l’intérieur de cette balise c’est là qu’on va référencer notre dictionnaire (ainsi que tous les autres dont nous aurions besoin dans la page en question).

Attention aux collisions !

Un ResourceDictionary pourrait présenter la/les même(s) clé(s) qu'un autre dictionnaire fusionné via Source (anciennement avec MergedWith) ou MergedDictionaries.

Une telle collision de noms doit être évitée justement en spécialisant les dictionnaires. Par exemple si un seul dictionnaire définit les couleurs de l’App et si les noms sont explicites (imaginons le suffixe ou le préfixe Color) peu de chances que des doublons apparaissent même dans un merge. Mais en revanche il est toujours possible qu’une collision apparaisse par exemple pour différents styles de boutons ou autres. Cela marque un manque de cohérence et de planification mais la cohérence absolue dans une vraie App n’existe pas ne nous leurrons pas…

Donc lorsque la page va essayer de retrouver une clé lors de l’application d’un style ou de l’utilisation d’une ressource elle utilisera l’ordre de précédence suivant :

  • Chercher dans les clés locales de la page
  • Chercher dans les Source (ex MergedWith) ResourceDictionary
  • Chercher dans chacun des dictionnaires fusionnés, dans l'ordre dans lequel ils sont répertoriés

La recherche s’arrête dès que la clé est trouvée… Pas de plantage donc, c'est plutôt une bonne chose, mais un comportement qui peut sembler "bizarre" et pas si évident que ça à déboguer ! Donc faites attention...

On voit qu’ici l’ordre d’utilisation des dictionnaires peut induire des comportements non souhaités et difficiles à déboguer… je ne peux que vous inciter à déclarer vos dictionnaires fusionnés qu’une seule fois dans App.Xaml ce qui les rend disponible partout dans l’application et assure qu’ils se présentent dans le même ordre à chaque fois. Mais cela n’interdit pas tous les cas figures (comme la déclaration de clés locales qui auront toujours la primauté mais cela suit une logique proche des classes par exemple et ne devrait pas vous perturber, cela peut même être utilisé astucieusement pour faire une sorte d’override local en cas de besoin et avec force documentation cela va sans dire !).

Impact ?

Toute bonne idée en programmation, toute nouvelle possibilité doit se juger à son efficacité et pas seulement à son esthétisme… Les dictionnaire fusionnés ont-ils un impact sur les performances ? C’est une bonne question à se poser avant de s’en servir !

La recherche dans les dictionnaires de ressources implique de vérifier chaque clé jusqu'à ce que la bonne soit trouvée. Si vous avez vraiment un grand nombre de clés et que la clé que vous recherchez se trouve exactement à la fin, et que vous utilisez cette clé plusieurs fois, cela risque d’affecter les performances soyons francs.

C’est pour cela qu’il existe deux écoles, celle présentée plus haut qui veut qu’on place tout d’emblée dans App.Xaml, ce qui évite les comportements bizarres difficiles à déboguer, et l’école de ceux qui pensent qu’il est préférable de ne placer que les dictionnaires de ressources appropriés dans chacune des pages afin d'éviter toute recherche inutile.

Les approches ont leurs avantages et leurs inconvénients, à vous de trancher en fonction du contexte, de la taille de vos dictionnaires, de la vitesse à laquelle les clés les plus “lointaines” doivent être utilisées et combien de fois, etc.

MergedWith ou Source ?

Il se trouve que WPF autant que UWP utilisent cette propriété sous le nom de Source et non pas MergedWith. Au début des Xamarin.Forms le dictionnaire de ressource possédait le tag MergedWith pour fusionner un autre dictionnaire de ressources. Il s’agit d’une divergence, autres temps, autres équipes de développement, tout ça… Mais depuis cette divergence a été gommée et MergedWith est marqué "deprecated" et la propriété Source est venue la remplacer, tout est rentré dans l'ordre ! Source fonctionne avec des URI ce qui le rend plus complet que la propriété originale et rend le XAML Xamarin.Forms plus homogène avec les autres XAML. 
Comment se fait la déclaration avec Source ?
Voici un exemple issu de la documentation Microsoft :

<Application.Resources>
  <ResourceDictionary>
    <ResourceDictionary.MergedDictionaries>
      <ResourceDictionary Source="StringResources.xaml" />
    </ResourceDictionary.MergedDictionaries>
  </ResourceDictionary>
</Application.Resources>

Pas plus compliqué que cela...

Conclusion

Les dictionnaires fusionnés sont une “arme” de choix pour structurer les ressources et les styles d’une application tout en pouvant les utiliser simultanément aussi bien qu’exploiter chaque dictionnaire séparément par souci d’efficacité.

Xamarin.Forms qui s'est parfois écarté XAML standard (le vrai le dur le tatoué, celui de WPF !) a fini par converger petit à petit. Le plus dur n’est pas forcément d’apprendre au départ à se servir d’une technologie mais une fois les habitudes prises d’être capable d’en suivre les évolutions et de s’adapter. La vie de développeur n’est pas de tout repos, mais vous la savez, sinon vous auriez choisi un autre métier Smile et vous ne seriez pas là à attendre avec impatience MAUI qui lui aussi va introduire ses petites divergences avec WPF et même avec les Xamarin.Forms (changement de namespaces par exemple) !

Stay Tuned !

blog comments powered by Disqus