[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.
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:
So, as you can see, you can’t bind a value directly on the ConverterParameter
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:
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 :
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
Happy Converters !
Comments
Anonymous
March 17, 2013
Hi Sebastien, if you add your converter to the resources of your itemtemplate/datatemplate the binding will be set before calling the Convert method.Anonymous
March 18, 2013
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)Anonymous
April 27, 2014
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?Anonymous
November 17, 2014
How can we use it with itemtemplate/datatemplate . DependencyProperty always give 0 in this case?Anonymous
May 11, 2015
Thanks so much, it helped me out! :-)Anonymous
February 09, 2016
Thanks you so much, you made my job easy :)