WPF Converter with Property vs MultiConverter? - wpf

What is the difference between using a Converter (IValueConverter) and passing in other values as parameters (ConverterParameter) vs using a MultiConverter (IMultiValueConverter) and just passing in multiple converter values?

There are two main differences. One is that ConverterParameter is not a Binding and does not listen for property changes, so the Binding won't refresh automatically if the value changes.
The other difference is that the ConverterParameter is an input to both Convert and ConvertBack, while all of the Bindings in a MultiBinding are inputs to Convert and outputs of ConvertBack. For example, if you are converting from DateTime to string, you might have the ConverterParameter be a format string, since that affects the conversion in both directions:
public class DateTimeConverter
: IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return ((DateTime)value).ToString((string)parameter, null);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return DateTime.ParseExact((string)value, (string)parameter, null);
}
}
On the other hand, if you want to convert from two doubles to a Size, then you would want to return two doubles when converting back:
public class SizeConverter
: IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return new Size((double)values[0], (double)values[1]);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
var size = (Size)value;
return new object[] { size.Width, size.Height };
}
}

Related

Binding to an array

I added a custom control library to my project and in that control there is an array DependencyProperty. Now when I try to bind that property in the client I get:
Tags of type 'PropertyArrayStart' are not supported in template sections.
The message is self explanatory but how do you set array properties in a DataTemplate?
You can use a IMultiValueConverter to build an array from multiple bindings. A converter is only required as you cannot use a MultiBinding without one.
public class MultipleValuesToArrayConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return values.ToArray();
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

Why does the MultiBinding not work for CornerRadius

If i use an IValueConverter it works, while with an IMultiValueConverter returning the same value it does not, why is that?
<Border Background="Red" Width="100" Height="100"
CornerRadius="{Binding Converter={vc:SingleAndMultiConverter}}" />
<Border Background="Red" Width="100" Height="100"
CornerRadius="{MultiBinding Converter={vc:SingleAndMultiConverter}}" />
public class SingleAndMultiConverter : MarkupExtension, IValueConverter, IMultiValueConverter
{
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return Convert();
}
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return Convert();
}
private object Convert()
{
return 15;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
}
The multi-binding throws this error:
Value produced by BindingExpression is not valid for target property.; Value='15'
Border.CornerRadius is of type CornerRadius. Value converters should always return the right type for the property.
It is hard to tell why they behave differently, presumably there is no default value conversion using type converters when using a multi-binding for some unexplained reason. If you were to dig into the source code you might find something but it probably won't be a pleasant journey.
What H.B. said +1
[ValueConversion(typeof(object[]),typeof(CornerRadius))]
public class Multi : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return new CornerRadius(Double.Parse("15"));
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

Silverlight binding - Apply Not or ! before binding to Checkbox.IsChecked property

I use to bind a bool variable of a class to a IsChecked property of Checkbox. What i want to do is to bind 'NOT' of the original value, like IsChecked = Binding Not(IsSelected), please let me know how this can be done.
Thanks
You need to use a Convertor. A convertor class implements IValueConverter and can convert the binding value into something else, in your case, negate it. You can do it like this:
public class BoolInverseConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is bool)
return !(bool)value;
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

Converter best practice for better performance

I'm using WPF converter and wondered in terms of performance what would be better in the following example, to use class members or local variables ?
public object Convert(object value, Type targetType, object parameter,System.Globalization.CultureInfo culture)
{
if ((int)value == 1)
return (Color)ColorConverter.ConvertFromString("#FCD44E");
return (Color)ColorConverter.ConvertFromString("#FCD44E");
}
or :
Color _color1 = (Color)ColorConverter.ConvertFromString("#FCD44E");
Color _color2 = (Color)ColorConverter.ConvertFromString("#FCD666");
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if ((int)value == 1)
return _color1;
return _color2;
}
The most performant would be to use private static readonly as follows
private static readonly Color Color1 = (Color)ColorConverter.ConvertFromString("#FCD44E");
private static readonly Color Color2 = (Color)ColorConverter.ConvertFromString("#FCD666");
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if ((int)value == 1)
return Color1;
return Color2;
}
See this answer for good discussion: Method can be made static, but should it?
While performance-wise the only relevant thing is to not do the conversion in every call to the Convert-method (as has been shown explicitly in the other answers), i would never write such a hard-coded converter in the first place, if you can parameterize something, do not hesitate to do so, e.g.:
public class OnValueToColorConverter : IValueConverter
{
public int Value { get; set; }
public Color OnValueColor { get; set; }
public Color OffValueColor { get; set; }
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return (int)value == Value ? OnValueColor : OffValueColor;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
}
<vc:OnValueToColorConverter Value="1"
OnValueColor="#FCD44E"
OffValueColor="#FCD666" />
(For this sort of thing one would normally not use a converter by the way but a Style with a default setter and a DataTrigger for the value on which it should change.)
The second option, but use static readonly fields if those colors are always constant. That way you're doing the work once, not every time you create a converter.

WPF Binding Formatexception

In a C# Wpf application I have an XML Binded datasource.
I want to update some xml like this:
loop.Innerxml = "false"
This value is binded (as a boolean) to a control. However when doing this, a formatexception comes up saying that a string is not a valid boolean (logical). However if the false is not entered as a string, I can't update the innerxml...
Any advice?
You can use a Converter to convert your Strings to Booleans when the binding happens.
For more about converters, see http://www.scip.be/index.php?Page=ArticlesNET22&Lang=EN.
Code sample:
[ValueConversion(typeof(string), typeof(bool))]
public class StringToBoolConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return TypeDescriptor.GetConverter(typeof(bool)).ConvertFrom(value);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
}

Resources