[Tips] WinRT Converter Parameter Binding

Today just a quick Tip on a frequently asked question:

How to Bind a parameter within a Converter (Which implements IValueConverter) ? And by the way, can we do this binding on the ConverterParameter property ?

 

ConverterParameter

To illustrate this example, here is my scenario :
I need, in my converter, to know 2 things :

  • Fom my current Item : the Distance property
  • From the item’s user : the TotalDistance property

Those two integer will help me to calculate the width of a rectangle representing the percentage of the current item distance.

image

I create and declared a DistanceConverter directly in the page ressources :

 <conv:DistanceConverter x:Key="DistanceConverter"  />     

And I use it in my ItemTemplate:

 <Rectangle HorizontalAlignment="Left" VerticalAlignment="Center" 
           Fill="#00FF84" Margin="10,0" Height="10"
           Width="{Binding Path=CurrentItem.Distance, 
 Converter={StaticResource DistanceConverter}, 
 ConverterParameter={Binding User.TotalDistance},
 Mode=TwoWay}">
</Rectangle>

The code of the DistanceConverter is trivial, and you ll get it in the sample provided with this post.

Be aware of the ConverterParameter : I tried to pass a value to the Converter using the ConverterParameter. Not a static value but the bindable value from my User instance.

But during the first execution, my parameter object is null:

imageSo, as you can see, you can’t bind a value directly on the ConverterParameter Sourire

The reason why is that the ConverterParameter IS NOT a depency property but a “simple” object. In this situation you can’t use Bindings.

This side effect is true with all XAML plateforms : WP7-8, Silverlight, WPF and of course WinRT.

Workaround

The idea is to create a dependency property on my converter and not using the ConverterParameter : To be able to create a DP, your converter must inherits from DependencyObject :

 public class DistanceConverter : DependencyObject, IValueConverter
{
    public UserViewModel CurrentUser
    {
        get { return (UserViewModel) GetValue(CurrentUserProperty); }
        set { SetValue(CurrentUserProperty, value); }
    }

    public static readonly DependencyProperty CurrentUserProperty =
        DependencyProperty.Register("CurrentUser",
                                    typeof (UserViewModel),
                                    typeof (DistanceConverter),
                                    new PropertyMetadata(null));

    public object Convert(object value, Type targetType, object parameter, string language)
    {
        throw new NotImplementedException();
    }

    public object ConvertBack(object value, Type targetType, object parameter, string language)
    {
        throw new NotImplementedException();
    }
}

Then, I declare my Converter in my page ressources :

    <common:LayoutAwarePage.Resources>
       <conv:DistanceConverter x:Key="DistanceConverter"  
           CurrentUser="{Binding User}"
           CurrentItem="{Binding CurrentItem}"
           MaxWidthAvailable="450" />
       
   </common:LayoutAwarePage.Resources>

And finally, I set my converter in my item Template :

 <Rectangle HorizontalAlignment="Left" VerticalAlignment="Center" 
           Fill="#00FF84" Margin="10,0" Height="10"
           Width="{Binding Path=CurrentItem.Distance, 
 Converter={StaticResource DistanceConverter}}">
</Rectangle>

Here is a screenshot of the property during the binding process:

image

Considerations

If you work with a “bindable converter”, remember this : Don’t create your converter directly in your ItemTemplate, like this :

 <Rectangle HorizontalAlignment="Left" VerticalAlignment="Center" 
           Fill="#00FF84" Margin="10,0" Height="10">
    <Rectangle.Width>
        <Binding Path="CurrentItem.Distance">
            <Binding.Converter>
                <conv:DistanceConverter
                    CurrentUser="{Binding User}"
                    CurrentItem="{Binding CurrentItem}"
                    MaxWidthAvailable="450" />
            </Binding.Converter>
        </Binding>
    </Rectangle.Width>
</Rectangle>

The binding on each instance of your converter will be too late, and the Convert method will be called BEFORE setting the value in your dependency property :

image

Other point of interest, If you need an other information from your current item, don’t try to get the current Item with the RelativeSource source like this:

 <conv:DistanceConverter x:Key="DistanceConverter"  
    CurrentUser="{Binding User}"
    CurrentItem="{Binding RelativeSource={RelativeSource TemplatedParent}}"
    MaxWidthAvailable="450" />

Because your converter is declared in the page ressources, RelativeSource on TemplatedParent doesn’t work Sourire

Happy Converters !

DependencyValueConverter.zip