More fun with Value Converters
I've blogged about IValueConverters and WPF many times in the past:
- IValueConverter and WPF
- ColorToBrushConverter
- Range converter for WPF
- How to display a formatted number in WPF
I've got another. This new one may seem a bit obscure, but it has proven useful. Here's what I wanted to do: conditionally, based on the enabled state of a button, swap between two different images, without special code, and without needing to use a style or a template; ideally the solution would not require an events or triggers.
So, I created the ConditionalStringConverter:
[ValueConversion(typeof(Boolean), typeof(string))] public class ConditionalStringConverter :IValueConverter { /// <summary> /// Converts a bound boolean value to a conditional string value. /// </summary> /// <param name="value">The boolean value (as a Boolean)</param> /// <param name="targetType">(ignored)</param> /// <param name="parameter">A string of the following format: /// {"value" Matches against this Boolean Value: True or False}?
{Return This Value if matches}:{Return this value if no match} /// For example: True?BackBtn.png:BackBtnDisabled.png /// If the bound boolean property is True, this converter returns
BackBtn.png. If False, it returns BackBtnDisabled.png /// </param> /// <param name="culture">ignored</param> /// <returns></returns> public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture) { if (string.IsNullOrEmpty((string) parameter)) { throw new ArgumentNullException(
"Converter Parameter must be specified when using
the ConditionalStringConverter."); }string[] condition = ((string)parameter).Split('?');
if (condition.Length != 2) {
throw new ArgumentOutOfRangeException(
"Converter Parameter improperly specified in ConditionalStringConverter."); }string[] values = condition[1].Split(':');
if (values.Length != 2) {
throw new ArgumentOutOfRangeException(
"Converter Parameter improperly specified in ConditionalStringConverter."); }
if (value is Boolean) {
bool compareValue = Boolean.Parse(condition[0]);if ((bool)value == compareValue) {
return values[0];
}
else {
return values[1];
}
}
else
{
throw new ArgumentException(
"ConditionalStringConverter converts only booleans", "value");
}
}public object ConvertBack(object value, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Before I walk through my simple solution, here it is in use:
<Button
x:Name="btnBack"
DockPanel.Dock="Top"
IsEnabled="{Binding Path=IsNotAtRoot}"
Click="btnBack_Click" Style="{DynamicResource ImageButtonStyle}">
<StackPanel
Orientation="Horizontal">
<Image
Source="{Binding Path=IsEnabled,
Converter={StaticResource conditionalConverter},
ConverterParameter=True?BackBtn.png:BackBtnDisabled.png,
RelativeSource={RelativeSource AncestorType={x:Type ButtonBase},
Mode=FindAncestor}}"
Height="24"
Width="24" />
<TextBlock
VerticalAlignment="Center"><Run Text="Back"/></TextBlock>
</StackPanel>
</Button>
The important part of the example that uses the new value converter is right here:ConverterParameter=True?BackBtn.png:BackBtnDisabled.png,
Based on the boolean binding, the value converter compares the value of the binding to the boolean value specified as the converter parameter. If the values match, the value converter returns the first value. If it does not, it returns the second value. If you're familiar with the C# conditional operator (?:), this should be familiar syntax.To make the image swap, I resorted to using a RelativeSource binding with an AncestorType of ButtonBase.
What's nice about this solution is that the Image's Source parameter accepts the String file name and happily converts it as necessary to the image (which in my application is a resource).And no, I'm not done with the button yet -- so it's a bit ugly.