Introduction
I originally
created a simpler form of this value converter for simple conversions of
Boolean properties to custom values, in particular converting Boolean to
Visibility. This was published in CodeProject as “Generic WPF/Silverlight Value
Converter” (http://www.codeproject.com/Articles/289495/Generic-WPF-Silverlight-Value-Converter). I got tired of
recreating the wheel for slight changes in the values, and cluttering up my
application with numerous value converters. Recently I was working on a new
project, and really did not want to add a Boolean property for each special
state. This would almost be as bad as adding a special property just for
Visibility. I got the idea of adding a third customizable property to my
converter. So now I have a much more flexible value converter.
Background
I originally
created the following very basic converter that allowed me to bind to a Boolean
property and return something else that was defined by setting two properties
on the value converter:
public class IfTrueValueConverter : IValueConverter
{
public object TrueValue { get; set; }
public object FalseValue { get; set; }
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
if (TrueValue == null)
{
TrueValue = true;
FalseValue = false;
}
return (bool)value ? TrueValue : FalseValue;
}
public object ConvertBack(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
if (TrueValue == null)
{
TrueValue = true;
FalseValue = false;
}
return (value.ToString() == TrueValue.ToString());
}
}
The XAML to use
this converter is very similar to using a simple converter except that true and
false values are defined as part of the defining of the converter in the
resources:
<Window x:Class="GenericValueConverter.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:GenericValueConverter"
Title="IfTrueValueConverter example" Height="350" Width="525">
<Window.Resources>
<local:IfTrueValueConverter x:Key="VisibilityConverter"
TrueValue="Visible" FalseValue="Hidden"/>
</Window.Resources>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBox Name="TestControl" Text="Test Message" Margin="5"
Visibility="{Binding IsVisible,
Converter={StaticResource VisibilityConverter}}"/>
</StackPanel>
</Window>
As can be seen,
first we need to define the value converter in the Window.Resources element of the XAML. This is just defining any value converter for use except there are two
additional arguments: TrueValue and FalseValue. All that is
needed is to put in the string value for the enumeration desired for when the
ViewModel’s value is true and false. The conversion capability of WPF is such
that it can convert these string values into the enumeration, so the converter
works. The converter can also be used to set colors, so string values of “Red”
and “Black” can be assigned to TrueValue
and FalseValue, and then the
converter can be used to set the Foreground Brush.
Using the converter
is now just like using any converter as can be seen in the XAML for the TextBox.
Type Conversion
When I was working
on improving this converter, I figured I could improve the performance by
taking the TrueValue and FalseValue properties, and
converting them to the correct types the first time, thus saving the time
required for the automatic conversion during the value converter conversion.
For this I used the TypeConverter class. This class
can be associated with the associated class it translates for by using an
attribute:
[TypeConverter(typeof(MyClassTypeConverter))]
public class MyClass
{
}
public class MyClassTypeConverter : TypeConverter
{
public override object ConvertBack(ITypeDescriptorContext context,
System.Globalization.CultureInfo culture,
object value,
Type destinationType)
{
}
}
Microsoft has type
converters for many of the standard classes. All that is required is to use the
static TypeDescriptor.GetConverter method, and then
the ConvertFrom method of the
returned class:
TypeConverter converter = TypeDescriptor.GetConverter(targetType);
return converter.ConvertFrom(value);
I used this conversion on both the TrueValue and FalseValue properties, which where both object. This meant that a simple cast is all that
is required to use the properties. The targetType argument in the
Convert method is used to determine what Type to convert the properties if the
Convert method is the first method to
execute (the ConvertBack method will also initialize the
values, but I think that this is probably redundant).
More Properties to Increase Flexibility
To meet the
immediate need for a converter I added what I initially called Compare (later
changed to CompareTrue). The idea was to compare the
property to the value argument passed in the Convert
method with this value, and if they are the same, then the TrueValue
is returned by the value converter Convert method. Now since
I will know the type of the value argument by
checking its type, I can use the TypeConverter on this property
also.
Having just this CompareTrue
property worked fine as long as I did not worry about the ConvertBack
method. For most everything I have used this converter for, I have never used
the ConvertBack method, but I should consider it.
Therefore I added a CompareFalse property.
Unfortunately I
then had to consider the fact that may be dealing with types where wanted to
compare null values. This added a fair amount of
complexity since I wanted a default value of true and false
if the type of the value argument in the Convert
method was Boolean, but there are valid cases for null for most types.
During initialization the CompareTrue and CompareFalse
properties are maintained at null unless they type
for comparison if Boolean, in which case it is changed to the Boolean value of true
and false, respectively. If the CompareTrue
and CompareFalse properties are not null then the values are
fixed using the type’s TypeConverter.
I added one last
extra property because I had a specific use for it as a default and that was a
null value. I have to admit that it added probably more complexity than worth
it for a general converter, but all value types can be made nullable, and
objects can be null.
Implementation
The code for the
converter is as follows:
public class IfTrueValueConverter : IValueConverter
{
public object TrueValue { get; set; }
public object FalseValue { get; set; }
public object CompareTrue { get; set; }
public object CompareFalse { get; set; }
public object NullValue { get; set; }
private bool _checkValues;
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
if (!_checkValues && value != null)
Initialize(targetType, value.GetType());
if (value == null) return
CompareTrue == null ? TrueValue:
(FalseValue == null ? FalseValue : NullValue);
return CompareTrue.Equals(value) ? TrueValue :
(CompareFalse == null ? FalseValue :
(CompareFalse.Equals(value) ? FalseValue : NullValue));
}
public object ConvertBack(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
if (!_checkValues && value != null)
Initialize(value.GetType(), targetType);
return TrueValue.Equals(value) ? CompareTrue : CompareFalse;
}
private void Initialize(Type targetType, Type compareType)
{
_checkValues = true;
TrueValue = (TrueValue != null) ? FixValue(targetType, TrueValue) : true;
FalseValue = (FalseValue != null) ? FixValue(targetType, FalseValue) : false;
NullValue = (NullValue != null) ? NullValue = FixValue(targetType, NullValue)
: null;
CompareTrue = (CompareTrue != null) ? FixValue(compareType, CompareTrue) : true;
CompareFalse = (CompareFalse != null) ? FixValue(compareType, CompareFalse) :
((compareType.FullName == (typeof(bool)).FullName) ? (object)false : null);
}
private static object FixValue(Type targetType, object value)
{
if (value.GetType() == targetType)
return value;
try
{
TypeConverter converter = TypeDescriptor.GetConverter(targetType);
return converter.ConvertFrom(value);
}
catch
{
DisplayIssue(targetType, value);
return value;
}
}
[Conditional("DEBUG")]
private static void DisplayIssue(Type targetType, object invalidValue)
{
if (targetType.IsEnum)
{
var enumNames = string.Join(", ", Enum.GetNames(targetType));
MessageBox.Show(string.Format(
"Enumeration value '{0}' not recognized for enumeration type '{1}'. Valid values are {2}.",
invalidValue, targetType, enumNames));
}
else
MessageBox.Show(string.Format(
"The value '{0}' not recognized for target type '{1}'.",
invalidValue, targetType));
}
The Example
The example shows the converter being used to change the foreground and background of some controls. The following shows the changes with each selection of the combo box.




The XAML for this is as follows:
<Window x:Class="ValueConverterExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:local="clr-namespace:ValueConverterExample"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ValueConverter Example"
Height="150"
Width="300">
<Window.DataContext>
<local:ViewModel />
</Window.DataContext>
<Window.Resources>
<local:IfTrueValueConverter x:Key="converterOne"
TrueValue="DarkGreen"
FalseValue="DarkGoldenrod"
NullValue="DarkRed"
CompareTrue="Green Color" />
<local:IfTrueValueConverter x:Key="converterTwo"
TrueValue="LightGreen"
FalseValue="LightYellow"
NullValue="Pink"
CompareTrue="Green Color"
CompareFalse="Yellow Color" />
</Window.Resources>
<StackPanel HorizontalAlignment="Center"
VerticalAlignment="Center"
Orientation="Horizontal"
Background="{Binding SelectedItem,Converter={StaticResource converterTwo}}">
<Label Content="Select a value"
Foreground="{Binding SelectedItem,
Converter={StaticResource converterOne}}" />
<ComboBox Name="comboBox"
Width="100"
SelectedItem="{Binding SelectedItem}"
ItemsSource="{Binding ItemsSource}"
Background="Transparent" />
</StackPanel>
</Window>
I have shown two cases, one where the CompareFalse is not set and the other where the
CompareFalse is set. There is
an obvious argument for a property that will be used in the Convert method when the value is neither equal to
CompareTrue or CompareFalse,
but I have not had a need yet, so did not implement such a property. Also, that would have raised some issues about how to handle the
ConvertBack return value.
Built In Debugging Assistance
You will have to
modify the XAML to show the debugging assistance that this code provides. All
that is required is that one of the properties like TrueValue or
FalseValue be assigned an
invalid value when the converter is defined in the XAML. If TrueValue for
Visibility is changed to
something like “illegal”, then the following MessageBox would appear:

This information
should provide a lot of help in fixing binding issues for enumerations. For
non-enumerations, the MessageBox is slightly simpler without the list of valid
enumeration values:

One of the nice things is that the dialog is only displayed the first time the converter is run.
There are other ways to provide feedback on translation issues, including writing to the Output window, but I prefer displaying a message to the developer.
Conclusion
This value converter can deal with simple Boolean comparisons, but this is more than adequate for most applications. Note many places you would use this converters
can be handled with triggers in WPF; Silverlight does not currently support triggers. However, I have found using this converter and then converting to
triggers makes things easier because I can use the debugger with converters and I cannot with triggers.
One issue I have found with debugging using this converter is that it is used for much more than a simple converter and sometimes debugging can be
difficult. I have used the ConverterParameter to help me with debugging.
Has been working as a C# developer on contract for the last several years, including 3 years at Microsoft. Previously worked with Visual Basic and Microsoft Access VBA, and have developed code for Word, Excel and Outlook. Started working with WPF in 2007 when part of the Microsoft WPF team. For the last three years has been working primarily as a senior WPF/C# and Silverlight/C# developer. Currently working as WPF developer with PIMCO in Newport Beach, CA.