See Sharp
Is it really just a matter of point of view ?

This silver is kinda light on the bindings : you can't do them with style !

February 2, 2010 14:00 by salfab

Depuis peu, voilà qu'il m'est donné la possibilité de jouer quelque peu avec Silverlight :

Beaucoup de choses disponibles en WPF ne le sont pas en Silverlight. Habituellement, des choses facilement contournables, mais la petite aventure du jour mérite certainement d'être consignée : 

Silverlight en tous cas jusqu'à la version 3 ne supporte pas le databinding dans les style setters.

Voyons un cas dans lequel ceci peut être embêtant :

Imaginons une ListBox dont on voudrait remplacer le pannel par défaut par un Canvas, de façon à y positionner les Items. Leur position serait définie par des coordonnées stockées dans les éléments eux-même, et exposées par une DependencyProperty.

Malheureusement, le contrôle ListBox encapsule ses éléments dans un ListBoxItem. Si l'on modifie le ItemTemplate pour ajouter les propriétés attachées Canvas.Top et Canvas.Left à l'élément racine du DataTemplate, l'effet ne sera pas appliqué.

Ceci s'explique par le fait que l'élément "racine" du DataTemplate ne sera pas directement dans le canvas : il sera contenu dans un ListBoxItem. Il faut donc définir les propriétés attachées Canvas.Left et Canvas.Top dans le ListBoxItem.

Ceci est faisable grâce à la propriété ItemContainerStyle qui contient le style du ListBoxItem.

<ListBox.ItemContainerStyle>
   
<Style>
       
<Setter Property="Canvas.Top" Value="{Binding Path=Y}" />
        
<Setter Property="Canvas.Left" Value="{Binding Path=X}" />
   
</Style>
</ListBox.ItemContainerStyle>

En WPF, ceci ne pose pas de problème, on peut définir avec un Style Setter, les propriétés attachées du Canvas, en leur appliquant un Binding.

En Silverlight, malheureusement, s'il est effectivement possible de donner des valeurs fixes pour les propriétés attachées du Canvas, il n'est pas possible de procéder de la sorte pour récupérer par Binding les coordonnées auxquelles placer l'élément : comme le DataBinding n'est pas supporté dans les style setter, il faudra trouver un moyen de créer ce binding dans le codebehind.

Ceci sera faisable en sous-classant le contrôle ListBox et en créant un override de la méthode PrepareContainerForItemOverride(DependencyObject element, object item), dans laquelle il sera possible de définir le Binding par code :    

        FrameworkElement contentitem = element as FrameworkElement; 
       
Binding leftBinding = new Binding("Left"); // "Left" is the property path that you want to bind the value to. 
        contentitem
.SetBinding(Canvas.LeftProperty, leftBinding); 
 
       
base.PrepareContainerForItemOverride(element, item); 

À noter que ItemsControl encapsule également ses éléments dans un ItemContainer, mais ItemContainerStyle n'est pas exposé par ItemsControl en Silverlight...