Dot.Blog

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

Comprendre l'Element Binding XAML/MAUI (ou l'Art du Bindind)

L’Element Binding est une nouvelle feature XAML déjà présente sous WPF qui n'est pas toujours bien comprise ou utilisée assez souvent. Pourquoi ? Et comment l'intégrer à ses pages XAML ?

L’Element Binding définit la capacité de lier les propriétés des objets entre eux sans passer par du code intermédiaire. Cette possibilité existait déjà sous WPF, on la retrouve dans MAUI bien entendu.

Pour simplifier prenons l’exemple d’un panneau d’information, par exemple un Border avec un texte à l’intérieur. Imaginons que l’utilisateur puisse régler l’opacité de cette fenêtre par le biais d’un Slider. 

Trois méthodes s'offrent à nous pour régler le lien entre le Slider et l'opacité du Border. J'appellerai la première "méthode à l'ancienne", la seconde "méthode du certifié" et la troisième "méthode MAUI". Les trois ont leur intérêt mais si vous êtes très pressé vous pouvez directement vous jeter sur la 3eme solution !

Méthode 1 dite « à l’ancienne »

Le développeur habitué aux vieux environnements comme Windows Forms aura comme réflexe immédiat d’aller chercher l’événement ValueChanged du Slider et de taper un code behind de ce type (après avoir donné un nom x:Name au Border) :

MonBorder.Opacity = MonSlider.Value ;

Cela a l’avantage d’être simple, efficace, de répondre (apparemment) au besoin et de recycler les vieilles méthodes de travail sans avoir à se poser de questions… Parfait pour pantoufler.

Méthode 2 dite « du certifié »

Ici nous avons affaire à un spécialiste. Son truc c’est la techno. Suivre les guide-lines et écrire un code qui suit tous les dogmes de l’instant est un plaisir intellectuel. Parfois ses solutions sont un peu complexes mais elles sont belles et à la pointe de la techno ! Sont-elles lisibles, maintenables ou même simplement nécessaires ? Pas toujours, mais après tout c'est un esthète, un artiste de la techno, la finalité de l'App l'intéresse moins que la beauté de son code. Il est compétent de ce point de vue, et peut même avoir de brillantes idées, mais il ne faut pas tout prendre de lui sans un certain recul...
Pour rappel c'est une situation totalement fictive, un prétexte à montrer la progression du code et les erreurs qu'on peut commettre en voulant en faire de trop.

Conscient que la solution « à l’ancienne » a un petit problème (en dehors d’être trop simple pour être « belle », elle est one way, le slider modifie l'opacité du border mais l'inverse ne fonctionne pas, ce qui peut être acceptable selon les cas) il va chercher une solution objet élégante répondant à l’ensemble des cas possibles couvrant ainsi le two way, considération technique trop technophile pour le développeur du cas précédent. Oui, car quitte à développer une solution complexe, autant qu'elle balaye assez large même si ici le two-way n'a que peu d'intérêt.

Forcément ça va se compliquer. C’est techniquement et intellectuellement plus sexy que la méthode « à l’ancienne » mais cela réclame un effort de compréhension et de codage (donc lisibilité moindre, maintenabilité pas évidente, etc).

Il faut en fait créer un objet intermédiaire. Cet objet représente la valeur du Slider auquel il est lié lors de son instanciation. Quant à l’objet Border, sa propriété Opacity sera liée par Data Binding standard à l’objet valeur.

Voici le code de la classe de liaison :

public class ValueBinder : INotifyPropertyChanged
       {
             public event PropertyChangedEventHandler PropertyChanged;
             private Slider boundSlider;
             public ValueBinder(Slider origine)
             {
                    boundSlider = origine;
             }
             public double Value
             {
                    get { return boundSlider==null?0:boundSlider.Value; }
                    set {
                           if (PropertyChanged!=null)
                                  PropertyChanged(this,
                                    new PropertyChangedEventArgs("Value"));
                           boundSlider.Value = value;
                    }
             }
       }

Cette classe, ou plutôt l’une de ses instances, servira à représenter la valeur courante du Slider. Ce dernier est lié à l’instance lors de la création de cette dernière (voir le constructeur de la classe ValueBinder ci-dessus).

Comment utiliser cette classe ?

La première chose est qu’il faut en créer une instance, cela peut se faire dans le code XAML ou bien dans le code behind de la façon suivante (dans le constructeur de la page par exemple) :

var valueBinder = new ValueBinder(slider);

Maintenant il suffit dans le code XAML de lier les deux objets à la valeur de la classe intermédiaire, par Data Binding :

Côté Slider, le code est :

<Slider x:Name="slider"Value="{Binding Value, Mode=TwoWay}"/> 

Côté Border :

<Border x:Name="borderInfo"Opacity="{Binding Value, Mode=TwoWay}"> 

Ne reste plus qu’à rendre visible l’objet intermédiaire par exemple en en faisant la valeur courante du DataContext de l’objet de rang visuel supérieur (ici un VerticalStackLayout appelé LayoutRoot) :

LayoutRoot.DataContext = valueBinder;

Et voilà ! Ne reste plus qu’à compiler et vous aurez le plaisir de voir que l’opacité du Border change bien lorsque le Slider est déplacé.

La chaîne est la suivante : la modification de la position du Thumb entraîne dans le composant Slider la modification de la valeur de la propriété Value. Comme celle-ci est liée par Data Binding TwoWay à la propriété Value de l’objet intermédiaire cette propriété va se trouver modifiée dans le même temps. Comme le Setter de la propriété notifie le changement de valeur de la propriété (la classe implémente INotifyPropertyChanged) et comme la propriété Opacity du Border est elle aussi liée par Data Binding à la propriété Value de l’objet intermédiaire, le Border sera « prévenu » du changement de valeur et sa propriété Opacité sera immédiatement modifiée pour recevoir la valeur courante de Value de l’objet intermédiaire ! C’est pas fun tout ça ? (hmmm j’en vois un qui ne suit pas là bas au fond… !).

Vous allez me dire, TwoWay, on veut bien te croire mais on ne le voit pas là … Pour l’instant cela se comporte exactement comme la première solution, juste qu’il faut avoir un niveau de certifié pour comprendre… C'est vrai mais c'est une approche complète qui peut fonctionner justement avec d'autres objets qui eux seraient two-way. On pourrait penser, pour le mettre en évidence ici à utiliser le changement de valeur de la variable liée à l'objet de suivi. Par exemple

borderInfo.Opacity = 0.8d;

Ce qui pourrait être déclenché par un clic de bouton, et vous verriez alors le slider se déplacer tout seul;

Cette solution semble élégante, complète mais complexe. 

Mais attendez ... N'est pas en train de se fourvoyer complètement ?

Bien sûr que si !

Et pour deux raisons :

  • d'une part nous travaillons dans le code-behind, ce qui pour manipuler l'UI est parfaitement licite mais ici le code ne fait rien, s'il fallait prendre des décisions, stocker la valeur du slider dans les préférences utilisateurs, etc, il faudrait agit depuis un ViewModel !
  • Et, d'autre part, nous complexifions de trop alors que le Binding XAML nous offre déjà tout le nécessaire. On l'utilise d'ailleurs, mais de façon tortueuse...

Méthode 3 dite « XAML/MAUI »

Comment marier le reflexe (plutôt sain) du premier développeur de notre parabole de vouloir faire vite et simple avec l’exigence intellectuelle (tout aussi saine) du second qui implique de faire « complet » ?

L’Element Binding sera ici la réponse : cela répond au besoin, et ce problème d'UI restera côté UI donc en XAML, pas de cofr-behind ni de ViewModel. 

La méthode MVVM ?

Bien entendu s'il est nécessaire de traiter la valeur de l'opacité, de stocker la position du slider, etc, il suffira d'utiliser un ViewModel exposant une valeur double à la quelle à la fois le Slider et l'opacité du Border seront liées. Et Le ViewModel pourra alors réagir et agir comme il le souhaite tout en respectant le découplage fort.

Mais ici réglons juste le problème posé de façon très simple, propre et directe et pour illustrer l'intérêt de l'Element Binding, regardons ce petit morceau de code :

 <Border BackgroundColor="Aquamarine" 
         BindingContext="{x:Reference Name=sliderOpacity}"
         Opacity="{Binding Value}">

     <VerticalStackLayout>
         <Label
         Text="Hello, World!"
         SemanticProperties.HeadingLevel="Level1"
         FontSize="32"
         HorizontalOptions="Center" />
         <Label
         Text="Welcome to .NET Multi-platform App UI"
         SemanticProperties.HeadingLevel="Level2"
         SemanticProperties.Description="Welcome to dot net Multi platform App U I"
         FontSize="18"
         HorizontalOptions="Center" />
     </VerticalStackLayout>
 </Border>
 <Slider x:Name="sliderOpacity" Minimum="0" Maximum="1" />

Et c’est tout ce qu’il y a à faire pour obtenir une solution complète, élégante, sans code behind et très facile à mettre en œuvre ! Merci MAUI (et WPF d'où provient l'idée) !

Conclusion

Tout est prétexte à présenter certains aspects du codage, certaines erreurs ou mauvaises approches et de voir des solutions plus élégantes. Les situations sont fictives mais sont des archétypes que vous retrouverez dans vos apps.

Je n'ai pas développé la solution avec ViewModel que je n'ai qu'évoquée. C'est vraisemblablement la plus courante aujourd'hui et vous la connaissez (ou avez déjà lu mes dizaines, ou centaines ?, de billets sur MVVM). Toutefois régler des problèmes d'UI en restant dans l'UI est un besoin réel. L'Element Binding en quelques mots de XAML règle ce problème. Il faut donc connaître ce binding particulier et savoir l'utiliser à bon escient.

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 !