Dot.Blog

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

Xamarin.Forms : Dictionnaires fusionnés

Très utiles pour stocker des styles et d’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 de  MergedWith . Avec MergedDictionaries, apparu plus récemment, 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 mon du nouveau fichier est arbitrairement (et avec une créativité folle) 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 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 esayer 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 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…

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.

Pourquoi MergedWith et non pas Source ?

Il se trouve que WPF autant que UWP utilisent cette propriété sous le nom de Source et non pas MergedWith. Pour l’instant dans Xamarin.Forms le dictionnaire de ressource possède le tag MergedWith pour fusionner un autre dictionnaire de ressources. Il s’agit d’une divergence, autres temps, autres équipes, tout ça… Mais ils le savent ! Et il est prévu que dans le futur le tag Source soit aussi utilisé dans Xamarin.Forms, toutefois cela devra attendre le support des dictionnaires pointés aussi par une URI. Histoire de faire les choses correctement. Mais dès que cette convergence aura lieu soyez sûr que MergedWith sera marqué “deprecated”… Surveiller les warnings de compilations au fil des mises à jour des Xamarin.Forms !

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 s’écarte encore parfois du XAML standard (le vrai le dur le tatoué, celui de WPF !) mais la convergence se fait 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

Stay Tuned !

blog comments powered by Disqus