.NET Tips and Tricks

Blog archive

Massaging Bound Data in WPF and Silverlight

I was recently asked by a WPF programmer how to modify a value passed between bound properties in a XAML file. You can't, for instance, integrate math into a binding expression, as this example does, to divide the value returned by 2:

<Image Height="100" Width="100" Name="MyImage" Source="Me.jpg" 
     Opacity="{Binding ElementName=MySlider, Path=Value/2}" /&rt;
This doesn't work, either:
<Image Height="225" Width="300" Name="CurrentImage" Source="Me.jpg" 
     Opacity="{Binding ElementName=MySlider, Path=Value}/2" /&rt;
While XAML gives you a lot of options when binding a property on one control to something else, modifying the data isn't allowed; at least, not directly. The answer is to use a custom converter. Programmers usually create converters to handle converting the data type of one property to the data type to another property (though XAML will handle a lot of conversions -- numerics to strings, for instance --on its own). But you can also use converters to massage the data in any way you want. This example, for instance, binds the Opacity setting for an Image control to the value in a slider control:
<Slider Margin="10" Name="MySlider" Minimum="0.0" 
     Maximum="2" Value="0.5"/&rt; 
<Image Height="100" Width="100" Name="MyImage" Source="Me.jpg" 
     Opacity="{Binding ElementName=MySlider, Path=Value}" /&rt;

Let's say that you want the transparency to be one-half of the value in the Slider: a converter will solve the problem. This converter, for instance, divides the number passed to it by 2 in its Convert method:

[ValueConversion(typeof(int), typeof(int))]
public class Divider : IValueConverter
{
    public object Convert(object value, Type targetType, 
        object parameter, System.Globalization.CultureInfo culture)
    {
        return object/2;
    }
    public object ConvertBack(object value, Type targetType, 
        object parameter, System.Globalization.CultureInfo culture)
    {
        return object * 2;        
    }
}

You need to do three things to use your converter. First, define a namespace for your converter (I've assumed that the converter is in the same project as the XAML file and the namespace for the project is MyApp):

<Window x:Class="MyClass"
  …
  xmlns:current="clr-namespace:MyApp"&rt;

Then you have to add your converter to your resources. I'm adding this converter to the Window's resources with the key MyDivider:

<Window.Resources&rt;
  <current:Divider x:Key="MyDivider"/&rt;
</Window.Resources&rt;

Finally, invoke your converter by passing the resource's key to the StaticResource object's constructor to retrieve it from the resources collection, and then passing that result to the Binding object's Converter property, like this:

<Image Height="100" Width="100" Name="MyImage" Source="Me.jpg" 
       Opacity="{Binding ElementName=MySlider, Path=Value 
               Converter={StaticResource MyDivider}}

Posted by Peter Vogel on 08/09/2012 at 1:16 PM


comments powered by Disqus

Reader Comments:

Mon, Aug 20, 2012 Philip

Yeah exactly. The power that xaml binding gives you can also lead you to the dark side if you're using them incorrectly or flippantly ;) You could also check the type of the targetType argument, and throw a suitable exception, to make sure that the xaml element using the converter uses a value of the same type that the converter returns. This would help in avoiding what you term a "binding blunder" from happening.

Thu, Aug 16, 2012 Peter Vogel Canada

Philip: That's EXCELLENT advice. One of the disadvantages of using converters is that it does spread the code around to places where it isn't...obvious...to the next developer (or even you in a few weeks). Binding makes the problem worse by putting the connection between the user's actions and the code being run "in the XAML file...somewhere." You can't make logical errors in XAML but you still can make what I call "blunders"--binding an inappropriate item is the classic blunder, I think. So the kind of defensive programming you're recommending really is essential to good programming practice.

Sun, Aug 12, 2012 Philip

An important point I think is that the convert method should check the type of the value argument and throw a suitable ArgumentException if its the incorrect type. You should also cast the value argument to the correct type as working with object when its not the expected type is dangerous. if(!(value is Double)) throw new ArgumentException("Only double types supported", "value"); var val = (double)value; etc... If, in your example code, you accidentally bound a string value to the target which couldn't be converted to a number, the converter would fail and subsequently the entire binding fails quietly and no one is any the wiser. This can put you in line for hours of hairpulling while you try to find the reason for unexpected behavior in your UI. I think this is useful to know, along with how to set breakpoints on bindings and be able to inspect the values, for anyone new to working with bindings.

Add Your Comments Now:

Your Name:(optional)
Your Email:(optional)
Your Location:(optional)
Comment:
Please type the letters/numbers you see above

.NET Insight

Sign up for our newsletter.

I agree to this site's Privacy Policy.