When starting with WPF, I had a hard time realizing when to use converters and just how powerful they can be. I would often create styles with complicated Data Triggers, or abuse my ViewModel instead of doing some simple converter magic.
I’ll share with you some converter tips and tricks that make my development easier every day.
Tip #1: When starting a new project, get a converters library
A lot of standard converters are reused in every WPF application. No need to recode the wheel. There are several open source libraries available we can use:
Tip #2: Write C# code in XAML with QuickConverter
This is a real gem. This library actually allows to write c# code in XAML.
Here’s an example:
<CheckBox IsEnabled="{qc:Binding '!$P', P={Binding Path=ViewModel.SomeBooleanProperty}}" />
Source: http://blog.danskingdom.com/?s=quickconverter
Or:
<TextBlock Text="{Binding Message}"
FontSize="{qc:Binding '$P == null ? $V2 : ($P.Length > 300 ? $V2 : $V1)',
P={Binding Message}, V1={StaticResource FontSize.Large},
V2={StaticResource FontSize.Small}}"/>
In this example the FontSize is changing according to Message’s length. If there’s over 300 characters, the FontSize will be smaller.
I used it in our code base and I think it’s awesome. It’s more readable and we need much less code for simple logic as in the examples above.
For documentation check out this post , the documentation on CodePlex and QuickConverter on NuGet .
This doesn’t cancel out regular converters. We still would want a lot of reusable converters as regular C# classes.
EDIT: Since writing this post I discovered a better-maintained library, CalcBinding , that achieves the same goal. I would now suggest using CalcBinding rather than QuickConverter.
Tip #3: Markup extension BaseConverter
Using a converter requires you to create an instance of the converter. The conventional way is using a StaticResource:
<Window.Resources>
<local:BooleanToVisibilityConverter x:Key="VisConverter"/>
</Window.Resources>
...
<Button Visibility="{Binding BtnVisilbe,
Converter={StaticResource VisConverter}}">OK</Button>
In the above example, we will use the same instance of the converter every time we use StaticResource. This is both good and bad.
Good because we might gain in performance for not having to create a new instance.
Bad because if we want to use parameters with out converter, those parameters will be the same every time we use the StaticResource.
Consider having the converters derive from MarkupExtension. We will be able to write like this:
<Button
Visibility="{Binding BtnVisible, Converter={local:BoolToVisibilityConverter}}">
OK</Button>
Our converter will have to derive from MarkupExtension:
class BooleanToVisibilityConverter : BaseConverter
{
...
}
abstract class BaseConverter : MarkupExtension, IValueConverter
{
public abstract object Convert(object value, Type targetType,
object parameter, System.Globalization.CultureInfo culture);
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
}
This is easier to read and will create a new instance of the converter every time. Which is usually what I want. I want to be able to give different parameters to my converter every time and the performance loss of creating a new instance is negligible.
Here’s a nice post that explains about converters as MarkupExtension in more detail.
Tip #4 Result Converter
Sometimes, we need to do a “double” conversion. Or, in other words, convert the result of our conversion.
Let’s use the example from Tip #2. In a TextBlock, we want to use a smaller font if the message is too long (More than 300 characters).
We saw how to do this easily with QuickConverter, but let’s assume we want to do it the old fashion way for whatever reasons.
Instead of creating a new custom converter, we can save some time and effort and use our standard converters. Like these two:
- StringLength converter: Returns number of characters in a string
- EqualityConverter: Accepts 3 parameters
- ToCompare
- Smaller: Value when Binding < ToCompare
- Bigger: Value when Binding >= ToCompare
Here’s the code that combines the above standard converters to our custom needs:
<TextBox Text="{Binding Message}"
FontSize="{Binding Message, Converter={local:StringLengthConverter
ResultConverter={local:EqualityConverter ToCompare=300, Bigger=14, Smaller=16}}}"
To be able to do that, we need to add ResultConverter to our BaseConverter class. Like this:
public abstract class BaseConverter : MarkupExtension, IValueConverter
{
public IValueConverter ResultConverter { get; set; }
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var result = InternalConvert(value, targetType, parameter, culture);
if (ResultConverter != null)
return ResultConverter.Convert(result, targetType, parameter, culture);
return result;
}
protected abstract object InternalConvert(object value, Type targetType,
object parameter, System.Globalization.CultureInfo culture);
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
}
This is really convenient, trust me. It will save time writing those extra unnecessary converters.
A note about the BaseConverter class:
In the last example we saw a BaseConverter that derives from MarkupExtension and uses ResultConverter. It is missing the methods ConvertBack and ConvertBackInternal to make the base class usable.
Also, I suggest having a base class for MultiValueConverer that has the same concepts.
Summary
I showed you some tricks that make my WPF development more productive and the code more readable.
I hope I gave you some value and maybe made converters look a bit more useful and easier.
Have a great day,
Michael