Dot.Blog

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

Silverlight : Debug du Binding Xaml

[new:30/08/2012]Ajouté dans Silverlight 5 le debug des bindings en Xaml est une possibilité encore peu utilisée alors qu’elle simplifie beaucoup la recherche et la résolution de bugs. Une bonne raison d’en parler un peu avec un petit exemple.

Binding Xaml

Le Binding en Xaml c’est comme l’oxygène pour un terrien : si vous l’enlevez, tout meurt...

MVVM est par exemple basé totalement sur l’exploitation du Binding.

Mais le Binding Xaml s’apparente à une extension du balisage, une sorte de langage dans le langage. J’ai déjà parlé dans le précédent billet des extensions personnalisées de Binding, vous vous en souvenez forcément. Peut-être avez-vous raté mon (gros) article sur le Binding “Le Binding Xaml, sa syntaxe, éviter ses pièges... (WPF/Silverlight)” paru en avril 2010 et toujours d’actualité ? Je ne saurais que conseiller au lecteur intéressé de se référer à ce papier très complet.

Bref, le Binding c’est essentiel, j’en ai déjà beaucoup parlé (comme de l’Element Binding aussi, en 2009 ce qui ne nous rajeunit pas Sourire).

Le problème du Binding c’est qu’il y a plusieurs façons de s’en servir : soit en le créant par les dialogues de Visual Studio ou de Expression Blend, soit parfois directement en code Xaml.

Dans le premier cas, il y a toutes les chances que le Binding soit correct (syntaxiquement au moins, mais pas forcément fonctionnellement...), dans le second cas s’ajoute la possibilité d’erreurs de frappe ou de confusion entre plusieurs classes ou propriétés.

Bugs sournois

Les bugs de Binding sont sournois par nature. Xaml n’étant pas compilé on voit d’ailleurs ici toute l’hérésie de vouloir développer des applications professionnelles énormes en Html/Js/CSS où rien n’est contrôlé ! Xaml n’échappe pas aux défauts des langages non fortement typés non compilés...

Les erreurs n’interviennent qu’au runtime et parfois elles sont mêmes difficiles à découvrir.

Quant à trouver la cause, c’est un jeu de piste hasardeux et long.

Il serait tellement simple de pouvoir mettre un point d’arrêt sur un binding et de vérifier si l’objet attaché, sa propriété, sont bien ceux qu’on attend...

Point d’arrêt !

Si ce n’est pas la panacée pour débugger tout code Xaml, Silverlight 5 (avec Visual Studio) a ajouté la possibilité de poser des points d’arrêt sur les Bindings.

Et çà change tout.

Exemple

Prenons un projet minimaliste où l’erreur est tout à fait possible. Et voyons comment le debug de Binding peut résoudre les choses en quelques secondes.

Il s’agit d’une application Silverlight 5 “normale” sans fioriture ni framework supplémentaire.

J’ai créé une classe “Book” qui permet de stocker les informations principales d’un livre :

public class Book
{
public string Title {get; set;}
public int Year { get; set;}
public string Author { get; set;}

public override string ToString()
{
return Title + ", " + Author + " (" + Year + ")";
}
}

Rien de bien savant. J’ai surchargé le ToString() pour simplifier l’affichage dans une ListBox sans avoir besoin de créer un DataTemplate. Je cherche la simplicité absolue pour cet exemple.

Dans le constructeur du code-behind de MainPage.xaml je lie le code de la classe MainPage au DataContext global de la page (c’est une forme ultra édulcorée “à l’arrache” de MVVM !) :

public MainPage()
{
InitializeComponent();
DataContext = this;
}

Bien entendu pas de données de Design ici !

Dans ce même code, j’instancie une liste de livres “en dur” avec une propriété publique pour y accéder :

public List<Book> Books
{
get
{
return books;
}
}
private List<Book> books = new List<Book>
{
new Book {Author = "Ray Bradbury",Title = "Chroniques martiennes",Year = 1950},
new Book {Author = "Jack Vance",Title = "Palace of Love",Year = 1967},
new Book {Author = "John T. Sladek",Title = "Roderick",Year = 1980},
new Book {Author = "Catherine Lucille Moore",Title = "Tout smouales étaient les Borogoves",Year = 1943}
};

Un peu de SF à lire pour les vacances si vous ne connaissez pas ces œuvres intéressantes...

Côté Xaml je place une ListBox dans le LayoutRoot, et je lie son ItemsSource à “Books” par Binding. Ce n’est pas ici que les choses vont aller mal alors passons.

Je place ensuite une Grid sous la ListBox. Elle contiendra les informations détaillées du livre sélectionné dans la liste.

Pour faire simple, je fais pointer le DataContext de cette grille sur le SelectedItem de la ListBox. Ce n’est pas là non plus que ça va dérailler.

A l’intérieur de cette grille, je pose des TextBlock, certains servent de labels, les autres vont être liés aux propriétés du livre sélectionné dans la liste.

C’est là qu’on peut facilement faire des bêtises. Autant jusqu’ici toute erreur de Binding sera très voyante (pas d’affichage dans la liste ou pas d’affichage du tout dans la grille de détail), autant dans les détails il va être possible de faire par exemple des fautes de frappes. Et comme la structure de notre exemple n’a aucun support de design pour les données (vous en voyez l’importance...) je vais taper les binding à la main.

Si je lance l’application et que je sélectionne une ligne dans la liste voici ce qu'i s’affiche :

image

La grille de sélection en haut, le détail en-dessous. Malheureusement l’année ne s’affiche pas... Dans cet exemple l’erreur se voit très vite, sur un écran chargé cela peut être plus difficile à détecter d’où l’importante d’une phase d’intégration bien menée. Mais là n’est pas la question. Il y a un binding qui ne marche pas...

<UserControl x:Class="xamldebug.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:xamldebug"
mc:Ignorable="d"
d:DesignHeight="372" d:DesignWidth="557"
DataContext="{Binding RelativeSource={RelativeSource Self}}">

<Grid x:Name="LayoutRoot" Background="White" >
<ListBox Height="145" HorizontalAlignment="Left" Margin="20,16,0,0" Name="listBox1" VerticalAlignment="Top" Width="437" ItemsSource="{Binding Books}"/>
<Grid Height="116" HorizontalAlignment="Left" Margin="20,167,0,0" Name="grid1" VerticalAlignment="Top" Width="352" DataContext="{Binding ElementName=listBox1, Path=SelectedItem}" Background="WhiteSmoke">
<TextBlock Height="23" HorizontalAlignment="Left" Margin="22,17,0,0" Name="textBlock1" Text="Titre" VerticalAlignment="Top" />
<TextBlock Height="23" HorizontalAlignment="Left" Margin="22,46,0,0" Name="textBlock2" Text="Auteur" VerticalAlignment="Top" />
<TextBlock Height="23" HorizontalAlignment="Left" Margin="22,82,0,0" Name="textBlock3" Text="Année" VerticalAlignment="Top" />
<TextBlock Height="23" HorizontalAlignment="Left" Margin="100,14,0,0" Name="textBlock4" Text="{Binding Title}" VerticalAlignment="Top" FontWeight="Bold" />
<TextBlock Height="23" HorizontalAlignment="Left" Margin="100,46,0,0" Name="textBlock5" Text="{Binding Author}" VerticalAlignment="Top" FontWeight="Bold"/>
<TextBlock Height="23" HorizontalAlignment="Left" Margin="100,82,0,0" Name="textBlock6" Text="{Binding Yer}" VerticalAlignment="Top" FontWeight="Bold" />
</Grid>
</Grid>
</UserControl>

Le code Xaml de la fiche est ultra simple et le TextBlock incriminé est le dernier objet déclaré. Un œil habitué à Xaml verra certainement tout de suite d’où vient le problème car ici tout est vraiment super simple...

Imaginons que cela soit plus proche de la réalité, plus complexe, plus difficile à décrypter et que vous soyez charrette, le truc à rendre pour avant-avant-hier, fin de journée dans un open space qui tue la concentration, 2 bonnes heures sup au compteur, tiens la femme de ménage vide les poubelles vous êtes tout seul (heures sup que le patron ne payera pas, forcément vous êtes “cadre” la belle escroquerie !), trois fois que votre nana essaye de vous joindre sur le portable et que vous refusez l’appel pensant terminer dans 5 minutes (depuis trois heures en fait), ça va encore être chaud ce soir en rentrant... Bref, une journée normale d’informaticien Sourire

Dans ces conditions là, la petite coquille en Xaml, elle devient vachement plus difficile à trouver je vous le garantie !

Mais heureusement le Binding depuis Silverlight 5 peut être debugger avec un point d’arrêt !

Plaçons un point d’arrêt sur la ligne du TextBlock récalcitrant, directement dans la page de code Xaml comme on le ferait en C# et lançons le programme...

image

Petit zoom sur la fenêtre “Output” :

image

Il y a une “erreur de chemin d’accès” dans l’expression de Binding sur la propriété “Yer” qui est introuvable sur le livre “Tout smouales étaient...”

Là c’est bon... “Yer” ça n’existe pas. Même fatigué ça n’existe pas. Bourré éventuellement, mais pas de ça chez nous !

Il suffit d’ajouter le “a” qui manque dans le code de Binding pour se lier à la propriété “Year” et tout est ok !

Bien entendu les expressions de binding sont parfois bien plus complexes, avec des sources relatives, ou des Path longs, etc. Pouvoir connaitre immédiatement le détail du Binding qu’on soupçonne d’aller de travers est un vrai gain de temps. D’autant que bénéficiant d’un vrai point d’arrêt nous pouvons utiliser tous les outils de debug pour inspecter les objets (parfois l’erreur ne vient pas de Xaml mais d’un objet sous jacent mal instancié, nul, etc...).

Conclusion

Même s’il n’y a rien de très spectaculaire dans le debug Xaml du Binding, et même si j’ai du ajouter un peu d’humour à ma façon pour rendre le sujet moins triste, il n’en reste pas moins vrai que techniquement c’est une nouvelle possibilité bien pratique qui peut faire gagner beaucoup de temps.

Tout ce qui peut améliorer le debug est toujours le bienvenu.

Stay Tuned !

blog comments powered by Disqus