ValueConverter et ConverterParameter dynamiques
Une contrainte à laquelle on fait face rapidement lors de l'utilisation de ValueConverters est celle qui force l'attribut ConverterParameter à être statique. Il n'est en effet pas possible de binder l'attribut ConverterParameter à une DependencyProperty, ce qui restraint énormément son usage. Ce post décrit une technique de contournement, voici son fonctionnement :
- Plutôt que d'utiliser l'attribut ConverterParameter, le ValueConverter expose une ou des propriétés standard(s) qui sont utilisée(s) par la méthode Convert(...)
- Les éléments UI permettant de changer les paramètres du ValueConverter sont bindés à ces propriétés
- Les changements de valeur des paramètres provoquent une réévaluation des bindings utilisant le ValueConverter
Dans le projet exemple, PaddingConverter peut être configuré par ses deux propriétés IsLeftPadding et TargetLength.
Ces deux propriétés sont modifiables par le biais respectivement d’une CheckBox et d’un Slider :
<CheckBox IsChecked="{Binding Source={StaticResource PaddingConverter},Path=IsLeftPadding}" …>
<Slider Value="{Binding Source={StaticResource PaddingConverter},Path=TargetLength}"…>
Le changement de valeur de ces propriétés ne peut cependant pas déclencher une mise à jour automatique de l’affichage car c’est toujours le Binding qui appelle le ValueConverter, et non le contraire.
On arrive ainsi à la partie la moins élégante de cette technique : il faut déclencher une réévaluation du Binding dans le code-behind dès qu’une propriété du ValueConverter change. Pour cela, PaddingConverter implémente l’interface INotifyPropertyChanged, et son parent (la fenêtre) s’abonne à l’évènement PropertyChanged du ValueConverter pour exécuter la méthode un BindingExpression.UpdateTarget() sur les éléments utilisant le PaddingConverter (le TextBlock dans notre cas).
var converter = this.FindResource("PaddingConverter") as PaddingConverter;
converter.PropertyChanged += new PropertyChangedEventHandler(
delegate(Object o, PropertyChangedEventArgs ea)
{
if (txt == null)
return;
// Il faut mettre à jour les endroits où le converter est utilisé
BindingExpression be = txt.GetBindingExpression(TextBlock.TextProperty);
be.UpdateTarget();
});
On pourra trouver d'autres manières de déterminer les éléments à mettre à jour (parsing du Xaml, parcours de l'arbre visuel), mais le principe de mise à jour de l'affichage restera le même.
En espérant que cette technique vous dépannera, rendez-vous au prochain post !