[Tips] WinRT Converter Parameter Binding

[Tips] WinRT Converter Parameter Binding

Rate This
  • Comments 3

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 !

Attachment: DependencyValueConverter.zip
Leave a Comment
  • Please add 6 and 4 and type the answer here:
  • Post
  • Hi Sebastien,

    if you add your converter to the resources of your itemtemplate/datatemplate the binding will be set before calling the Convert method.

  • If you add your converter directly in your ItemTemplate, it won't be a static Converter, so the object will be instantiate after bindings occurs.

    That's why i have declared it in the Page Ressources (it will be a static ressource in this particular case)

  • Hi Sebastian,

    I have followed the above and bound my value converter to a value in the viewModel. It gets the first initial value when the page is first called, so I at least know its hooked up correctly. But when I change the bound object and raisePropertyChanged manually it does not re-fire the converter in which the binding is held.

    Any ideas?

Page 1 of 1 (3 items)