Understanding the Converter in DataBinding

Abhi2434
Posted by in WPF category on for Beginner level | Views : 14532 red flag
Rating: 2.5 out of 5  
 2 vote(s)

Converter and ConverterParameter comes very handy while working with WPF Data Binding. You can manipulate the data bound to a value using IValueConverter. The article demonstrates how you can use it in your solution with a sample application.


 Download source code for Understanding the Converter in DataBinding

Introduction
Guys, While working with WPF, I have found lots of interesting things that I think I should discuss. All of us are well aware of superior Binding Capabilities that is supported with WPF. I found it very exciting when I saw it for the first time. It seemed to me a great way to define everything in XAML rather than handling events so much in Code-Behind. Right now, I try everything that regards to UI level changes, I always try to do it using XAML first.

XAML comes with lots of Markup Extensions. Anything that is placed within the curl braces ( { } ) are defined as Markup Extensions. They are derived from the Class MarupExtension, and the properties are defined directly for them within the brackets. Binding is also a Markup Extension for which ElementName, Path, etc are members. Among the members, Converter and ConverterParameter is what we are going to discuss here in this article.
Why We Require Converter?

Converter is required to enhance the capabilities that you get with the default Binding Markup extension. Say for instance you want to bind a property of an object with another. Say you want your border will have a width exactly what your Window Width is, in that case you will write like :

<Border Width="{Binding ElementName=yourwin, Path=ActualWidth}" />

Writing this will ensure that the width of the border to be equal to the actualwidth of the window. But suppose you want the border to be something less than the ActualWidth. Or say you want to change the color of the Border Brush based on the size of the Window ?
Here lies the importance of Converter.
IValueConverter

Converter is actually a property within the Binding Markup Extension. Its type is defined as IValueConverter. Every time the bound data elements refreshes its value it passes through a method Convert which is defined inside the object Converter (if found). Thus Converter acts as an interface to manipulate the actual data before passing it to the property.

public class MyConverter : IValueConverter
{
#region IValueConverter Members

public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
double passedValue = (double)value;
double parameterValue = (double) parameter;
return passedValue - parameterValue;
}

public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{

double passedValue = (double)value;
double parameterValue = (double)parameter;
return passedValue + parameterValue;
}

#endregion
}

Here in the code above I have defined a converter MyConverter, which does nothing but implements IValueConverter, which enables you to make use of it with Converter. The Converter here does nothing but implements two methods, Convert and ConvertBack. Convert is called when the value of the property is updated when the bound object value is changed. ConvertBack will only be called when the Binding Mode is set to TwoWay, and will be called only when the bound property changes its value.  The object value points to the value of the property passed in, TargetType is the type you have to return to the Target, parameter is defined from ConvertParameter and Culture is the UICulture.

To use the Converter you need to create a resource of the object. Thus in XAML you need to declare :

<local:MyConverter x:Key="resMyConverter" />

Where local is the namespace added which points to the Current Namespace. Finally to define the Converter, you need to use this as StaticResource in the Converter.

Height="{Binding ElementName=win, Path=ActualHeight, Mode=OneWay, Converter={StaticResource resMyConverter}, ConverterParameter=10}"  

Remember one thing, in the above line, you need to make sure you are clear about what you are sending. In this case, we are binding Height with the ActualHeight of the element win. So, in the arguments of Convert, you will receive a double value with current ActualHeight, and you are going to return the value that you want to be set to Height. So actually you are sending a double value and returning another.



The above figure demonstrates how you are going to send data to Convert method. The ActualWidth (Marked with Blue) is sent to object value, TargetType will be Double which is based on the Type of Width Property, parameter receives the value 50 passed with ConvertParameter, and the culture is the UICulture of the Current Thread.

The ConvertBack is used to be called only when you need the reverse. So if your Mode is defined as OneWay, your ConvertBack is not actually required.

Using the Sample



I have built the sample application to show you a few things that is possible only with the Converter. I have extensively used Converter, and you are free to send me any better idea of sample application, if you didn't find it much interesting.

To try the application :

  • Start the project, you will see the window with a border smaller than the actual window with Green Background and Converter Sample written over it.
  • Just as you resize it increases size of the ActualWidth of the Window, and it changes the Brush from LinearGradientBrush to RadialGradientBrush
  • Notice the Border as well as the FontSize increases its size with the change of size of the Window.

Implementation

In the sample application, I have added 3 Converters.

  1. To get Height / Width of the Border.
  2. To get the Size of the Text written inside the Box so that it doesn't goes out the visual area.
  3. To change the Color of the Background

1. Reset Height / Width of the Border


First let us see how we have implemented the Resize issue with Height/ Width of the Border. You might wonder why I did not used the Margin for this, but this is purely because I wanted to make you understand how you can use Converter. In real life you should implement this easily using Margin. 

[ValueConversion(typeof(double), typeof(double))]
public class AbsoluteConverter :IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
double actualValue = System.Convert.ToDouble(value);
double converterParameter = System.Convert.ToDouble(parameter);
return actualValue - converterParameter;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException("This will never be called as our Binding is OneWay");
}
#endregion
}

In the code above I have implemented a Converter that takes in a double value and returns a double. For defining the Width / height of the Border I used the reference of the Parent Window. As defined above, we need to add a Resource to instantiate the object of AbsoluteConverter. To do this I have added,

<local:AbsoluteConverter x:Key="objAbsoluteConverter" />

The objAbsoulteConverter (You can also create object in the Code behind and expose using a Property) is an instance of AbsoluteConverter that we are going to use. Since, I have used parameter to define the actual Width/Height we need, I need to pass ConverterParameter as well.

<Border Width="{Binding ElementName=win, Path=ActualWidth, Mode=OneWay, Converter={StaticResource objAbsoluteConverter}, ConverterParameter=50}" 
       Height="{Binding ElementName=win, Path=ActualHeight, Mode=OneWay, Converter={StaticResource objAbsoluteConverter}, ConverterParameter=100}" />


So the Border object looks like this. The Width gets 50 lesser than Width of the Window, and Height gets 100 less than the Height of the window.

Remember : ConverterParameter is not a DependancyProperty, so you cant Bind it with other objects.

2. Set FontSize based on Size of Window

Similar to that, let us define the Fontsize of the Text written inside the Border element to restore its size when window size changes. This behavior is missing in WPF, which is present with Anchor property for Windows Applications. Here I am going to put the FontSize 10% of the actual Window size.

[ValueConversion(typeof(double), typeof(double))]
public class SizeConverter :IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
double actualWidth = (double)value;
return actualWidth * .1;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException("This will never be called as our Binding is OneWay");
}
#endregion
}

So here you can see I have passed 10% of the actual width. You need to adjust the textsize based on the ratio.

3. Set Brush based on Size of Window

In the final step, I have tried to change the Actual Brush of the Window based on the Size of the window. Let us look at the code below :

public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
double sentValue = (double)value;
Brush br;
if (sentValue > 500)
br = new RadialGradientBrush(Colors.Red, Colors.Green);
else if (sentValue > 300)
br = new LinearGradientBrush
{
GradientStops = new GradientStopCollection{
new GradientStop(Colors.Red, 0),
new GradientStop(Colors.Green,1)
}
};
else
br = new SolidColorBrush(Colors.Green);
return br;
}

You can see here I am changing the Brush based on the value passed in. If the value is greater than 500 it will have a RadialGradientBrush, if its between 500 and 300 its LinearGradientBrush, else SolidColorBrush. Thus the final XAML will look like :

<Grid Background="Black">
<Grid.Resources>
<local:AbsoluteConverter x:Key="objAbsoluteConverter" />
<local:BrushConverter x:Key="objBrushConverter" />
<local:SizeConverter x:Key="objSizeConverter"/>
</Grid.Resources>
<Border x:Name="brd"
Width="{Binding ElementName=win, Path=ActualWidth, Mode=OneWay, Converter={StaticResource objAbsoluteConverter}, ConverterParameter=50}"
Height="{Binding ElementName=win, Path=ActualHeight, Mode=OneWay, Converter={StaticResource objAbsoluteConverter}, ConverterParameter=100}"
Background="{Binding ElementName=win, Path=ActualWidth, Converter={StaticResource objBrushConverter}}"
BorderThickness="2" >
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock Text="Converter Sample" FontFamily="Monotype Corsiva" FontSize="{Binding ElementName=win, Path=ActualWidth, Converter={StaticResource objSizeConverter}}" />
</Grid>
<Border.Effect>
<DropShadowEffect Color="#BBBBBB" ShadowDepth="15" BlurRadius="15"/>
</Border.Effect>
</Border>
</Grid>

Here you can see I have used objAbsoluteConverter for Width/Height, objBrushConverter for Background and objSizeConverter for FontSize of the TextBox.

You should try the sample application and check out how it is working.

Conclusion
Converter comes very handy to me at times and I hope this would be same for you. Always make sure what you are passing as parameter and what you intend to get while you define your custom Converter.

I would love to see your feedbacks for this post. 

Page copy protected against web site content infringement by Copyscape

About the Author

Abhi2434
Full Name: Abhishek Sur
Member Level: Silver
Member Status: Member,Microsoft_MVP,MVP
Member Since: 12/2/2009 4:19:08 AM
Country: India
www.abhisheksur.com
http://www.abhisheksur.com
Working for last 2 and 1/2 years in .NET environment with profound knowledge on basics of most of the topics on it.

Login to vote for this post.

Comments or Responses

Login to post response

Comment using Facebook(Author doesn't get notification)