Dot.Blog

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

Ne binder qu’une fois pour aller plus vite !

Sous Xamarin.Forms les bindings sont par défaut en OneWay, ou en TwoWay pour les éléments de saisie, ainsi on laisse le OneWay partout car il n’y a rien à faire… C’est une erreur. Pour améliorer les performance on peut faire beaucoup mieux, comment ?

Les bindings XAML

Je ne reviendrai pas sur les spécificités du Binding Xaml car j’en ai parlé longuement dans de nombreux articles et dans de nombreux livres dont la collection “All Dot Blog” de livres PDF gratuits (notamment dans le Tome 11 sur les Xamarin.Forms mais surtout dans le Tome 7 sur XAML), je ne peux que vous encourager à vous plonger dans cette saine lecture pour en apprendre plus !

Toutefois, et pour la bonne cause de cet article, voici un bref rappel sur les bindings car il en existe de plusieurs types :

  • le OneWay qui relie dans un seul sens une propriété de ViewModel à un contrôle d’UI afin que celui reflète les changement du ViewModel automatiquement
  • Le TwoWay qui relie propriété du ViewModel et contrôle d’UI dans les deux sens, cela n’est utile que pour les contrôle qui peuvent retourner des informations comme un éditeur de texte, un slider, une case à cocher…
  • Et enfin le OneTime. C’est une sorte de OneWay mais qui ne marche qu’une fois. Il met à jour une fois l’UI et puis il ne cherche pas à tracker les changements.

On pourrait se dire que c’est bien normal que le OneTime soit si peu utilisé et qu’il ne soit pas le comportement par défaut, ça ne sert à rien ce truc !

tss... tss…

D’abord si j’en parle c’est que forcément il y a des choses à en tirer, c’est un bon indice Smile et puis si depuis WPF on se “traîne” cette forme de Binding alors même que le XAML des Xamarin.Forms n’a même pas été développé à l’origine par Microsoft c’est qu’il doit bien y avoir des raisons solides de le conserver. Second indice qu’il y a anguille sous roche…

Et en effet, tous ces indices nous forcent à nous intéresser aujourd’hui à cette forme de binding si peu utilisée. Et à tort (sinon je n’en parlerai pas, revenir au premier indice plus haut !).

Le binding OneTime permet ainsi d’informer l’UI du contenu d’une propriété ViewModel, une seule fois, au moment de la création de la page. C’est un binding comme les autres (avec formatage, convertisseur etc) mais un binding sans lendemain. Un binding qui oublie la source, et une source qui se moque de la destination… Pas très fraternel tout ça… Et pourtant c’est utile !

Partons d’exemples

Le plus simple pour démontrer les avantages de ce binding un peu frustre c’est de le voir en situation. Que peut-il nous apporter ? De meilleures performances. Pourquoi ? parce qu’il ne crée aucun lien d’écoute (objet interne de binding qui maintien les liens, pose des écoutes pour voir les changements etc). Mais dans quelles situations l’utiliser ?

C’est justement ce que je vais vous montrer maintenant !

Les commandes

Voici un excellent client pour démontrer mon propos. En effet une commande est exposée comme une propriété et utilisée dans un binding côté XAML. Or, vous changez souvent l’objet commande en cours d’exécution d’un ViewModel vous ? Mettre à jour éventuellement la Lambda qui est exécutée pourquoi pas (et encore) mais l’objet Command lui-même on voit mal à quoi cela servirait d’en recréer dans un même ViewModel, c’est une sorte de constante généralement initialisée dans le constructeur. Une propriété qu’on doit d’ailleurs mettre en read-only car hors du constructeur il est rare de créer des commandes à la volée (il existe des exceptions, forcément).

Bref vous commencez à voir le tableau : toutes les commandes sont créées comme des propriétés et sont bindées en XAML comme telles, en utilisant le mécanisme par défaut c’est à dire le OneWay.

Il se crée alors un objet de binding en mémoire qui va poser une écoute sur le changement de la propriété, changement qui ne viendra jamais. Un objet qui sera détruit, enfin abandonné plutôt, en même temps que sa page, puis un jour collecté par le Garbage Collector s’il en a envie (voir mes autres articles sur le fonctionnement du GC).

Puisque l’on sait dès le départ que le lien entre l’objet d’UI et la propriété Command n’a d’intérêt qu’une seul fois… utilisons un binding OneTime !

Imaginez, rien qu’ici tous ces objets de bindings que je viens de supprimer de la mémoire de vos Apps ! Soulageant d’autant la mémoire et le processeur. Ajoutez à cela un chargement plus rapide (moins d’objets à créer) et multipliez cela par le nombre de page de votre App et le nombre de fois qu’elles sont créées en une journée par un utilisateur… Cela fait déjà un joli gain pour si peu de travail.

Travail ? Oui, un petit peu, puisqu’au lieu de laisser le binding par défaut type
{Binding MaCommande}
il faudra préciser
{Binding MaCommande, Mode=OneTime}

Pas de quoi demander une augmentation non plus nous sommes d’accord… Par contre pour y avoir pensé et l’avoir fait, là ça se discute sérieusement…

Les données statiques

Très fréquemment on utilise des données statiques, c’est à dire des données qui ne changeront pas. Vous me voyez venir ? Des exemples ? Oui par exemple une liste de catégories de produits, on charge la liste depuis une source de données mais pour le ViewModel c’est une liste de données statiques, ni lui, ni l’utilisateur ne va bricoler cette liste qui servira uniquement à faire des choix (dans une fiche produit, une interrogation de produits, des stats, etc).

De fait une telle liste peut et doit être en binding OneTime…

Imaginons :

public List<Person> Persons {get; set;} = new List<Person>()
{
  new Person() {Name = "Ella Lesfoix", Photo="ella.png"},
  new Person() {Name = "Bernard Tichaud", Photo="bernard.png"}
};

Le tout utilisé dans une vue Carousel :

<CarouselView ItemSource="{Binding Persons, Mode=OneTime}">
  <CarouselView.ItemTemplate>
    <DataTemplate x:DataType="models:Person">
      <Grid RowDefinitions="*, 32">
        <Image Source="{Binding Photo, Mode=OneTime}" />
        <Label Grid.Row="1" Text="{Binding Name Mode=OneTime}" />       
      </Grid>
    </DataTemplate>
  </CarouselView.ItemTemplate>
</CarouselView>

Combien d’objets de bindings évités ici ? Si la liste est chargée souvent, si elle est assez grande, cela pèse. Petite amélioration par petite amélioration l’App ira plus vite et consommera moins de mémoire, et c’est une chose à surveiller à chaque page qu’on développe dans une App mobile.

D’autres exemples

Je pense que vous avez compris le principe, inutile d’aller plus loin. On peut l’appliquer dans de nombreuses situations (comme dans le DataTemplate d’une collectionView pour les éléments qui ne changeront pas, ou tout ce qui ne bougera plus jusqu’à la recréation de la page en fait).

Pourquoi est-ce si peu utilisé ?

On aborde ici l’aspect plus “philosophique” du problème. Le plus intéressant à mes yeux car c’est en comprenant le pourquoi d’un problème qu’on peut le régler. Se gaver de code source n’aide pas à prendre du recul et à réfléchir sur ses propres méthodes de travail. Or c’est ce qui compte le plus.

J’ai déjà donné des éléments de réponse en introduction, le mode par défaut est OneWay, sauf pour les entrées de texte ou le TwoWay est utilisé par défaut sans qu’ait besoin de le préciser (c’est dans le code du contrôle qu’on précise quel mode de Binding est celui par défaut). De fait on ne se pose pas la question.

Mais en dehors de la paresse intellectuelle et le poids des habitudes, rien de plus ?

Oui et non. Oui car il y a une autre raison, mais non car au final elle revient à de la paresse intellectuelle. Je veux parler du syndrome “comme je ne sais pas je mets tout au max” C’est le principe des grilles de saisie notamment. Mauvaise analyse des besoins de l’utilisateurs alors on colle une grille, comme ça le user verra tout (sauf qu’il ne lit pas des centaines de lignes inutilement remontées de la base de données en plus). Ici c’est la même chose. “Peut-être, un jour, on ne sait pas, la valeur de la propriété devra changer en cours d’exécution et si on utilise OneTime on aura un bug (le changement ne sera pas visible) et comprendre que ça vient du binding en OneTime prendra du temps, alors autant laisser le mode par défaut, qui peut le plus peut le moins !”. Certes… Avec des raisonnements pareil il faudrait un super-calculateur pour afficher l’heure…

Donc là on touche à la fois à la paresse intellectuelle, à la mauvaise qualité globale des analyses, quand il y en a, au bordel généralisé qu’on appelle du doux nom de “programmation agile” (dans un mode totalement dévoyé à la décharge de cette méthode de travail) et donc on s’approche de sujets qui s’éloignent de la simple technique pour aborder la responsabilité du management des projets ce qui est une autre facette de notre métier.

Je sais, bien entendu, les conditions de travail très imparfaites sont le quotidien de très nombreux développeurs qui font avec. Je ne leur jette pas la pierre. Mais il ne faut pas s’étonner ensuite si les App sont lentes à charger, lourdes à maintenir, et lentes à l’exécution… L’important reste de savoir pourquoi, et un jour peut-être d’y remédier !

Conclusion

Je ne voulais pas conclure d’une façon qui donne l’impression que je mets en cause les méthodes de travail des uns ou des autres, d’ailleurs je ne suis pas un juge, juste un conseiller, je montre ce qui en va pas et je peux dire pourquoi, à chacun de penser comment cela peut l’aider à mieux développer. En humaniste je pense que tout le monde peut s’améliorer et que c’est par la connaissance de ses propres errances qu’on peut utiliser tout son potentiel. Gnothi Seauton, gravé sur le temple de Delphes, “connais-toi toi-même” (…”et tu connaîtras l’univers et les dieux” ajouté plus tard pour expliquer cette démarche très socratique). Voilà un beau sujet sur lequel travailler, l’informatique mène à tout et pose des questions qui dépassent philosophiquement les débats sur l’utilité de la surcharge des opérateurs en C# … ou de l’utilité de OneTime dans les bindings !


Stay Tuned !

blog comments powered by Disqus