4 minute read

If you’ve used binding at all in WPF then you more then likely have also written a converter. There are lots of tutorials on creating converters, so I’m not going to discuss that in length here. Instead I want to spread the word about a little known gem called QuickConverter. QuickConverter is awesome because it allows you to write C# code directly in your XAML; this means no need for creating an explicit converter class. And it’s available on NuGet so it’s a snap to get it into your project.

A simple inverse boolean converter example

As a simple example, let’s do an inverse boolean converter; something that is so basic I’m surprised that it is still not included out of the box with Visual Studio (and why packages like WPF Converters exist). So basically if the property we are binding to is true, we want it to return false, and if it’s false, we want it to return true.

The traditional approach

This post shows the code for how you would traditionally accomplish this. Basically you:

  1. add a new file to your project to hold your new converter class,
  2. have the class implement IValueConverter,
  3. add the class as a resource in your xaml file, and then finally
  4. use it in the Converter property of the xaml control. Man, that is a lot of work to flip a bit!

Just for reference, this is what step 4 might look like in the xaml:

<CheckBox IsEnabled="{Binding Path=ViewModel.SomeBooleanProperty, Converter={StaticResource InverseBooleanConverter}" />

Using QuickConverter

This is what you would do using QuickConverter:

<CheckBox IsEnabled="{qc:Binding '!$P', P={Binding Path=ViewModel.SomeBooleanProperty}}" />

That’s it! 1 step! How freaking cool is that! Basically we bind our SomeBooleanProperty to the variable $P, and then write our C# expressions against $P, all in xaml! This also allows us to skip steps 1, 2, and 3 of the traditional approach, allowing you to get more done.

More examples using QuickConverter

The QuickConverter documentation page shows many more examples, such as a Visibility converter:

Visibility="{qc:Binding '$P ? Visibility.Visible : Visibility.Collapsed', P={Binding ShowElement}}"

Doing a null check:

IsEnabled="{qc:Binding '$P != null', P={Binding Path=SomeProperty}"

Checking a class instance’s property values:

IsEnabled="{qc:Binding '$P.IsValid || $P.ForceAlways', P={Binding Path=SomeClassInstance}"

Doing two-way binding:

Height="{qc:Binding '$P * 10', ConvertBack='$value * 0.1', P={Binding TestWidth, Mode=TwoWay}}"

Doing Multi-binding:

Angle="{qc:MultiBinding 'Math.Atan2($P0, $P1) * 180 / 3.14159', P0={Binding ActualHeight, ElementName=rootElement}, P1={Binding ActualWidth, ElementName=rootElement}}"

Declaring and using local variables in your converter expression:

IsEnabled="{qc:Binding '(Loc = $P.Value, A = $P.Show) => $Loc != null &amp;&amp; $A', P={Binding Obj}}"
  • Note that the “&&” operator must be written as &amp;&amp; in XML.

And there is even limited support for using lambdas, which allows LINQ to be used:

ItemsSource="{qc:Binding '$P.Where(( (int)i ) => (bool)($i % 2 == 0))', P={Binding Source}}"

Quick Converter Setup

As mentioned above, Quick Converter is available via NuGet. Once you have it installed in your project, there are 2 things you need to do:

1. Register assemblies for the types that you plan to use in your quick converters

For example, if you want to use the Visibility converter shown above, you need to register the System.Windows assembly, since that is where the System.Windows.Visibility enum being referenced lives. You can register the System.Windows assembly with QuickConverter using this line:

QuickConverter.EquationTokenizer.AddNamespace(typeof(System.Windows.Visibility));

In order to avoid a XamlParseException at run-time, this line needs to be executed before the quick converter executes. To make this easy, I just register all of the assemblies with QuickConverter in my application’s constructor. That way I know they have been registered before any quick converter expressions are evaluated.

So my App.xaml.cs file contains this:

public partial class App : Application
{
    public App() : base()
    {
        // Setup Quick Converter.
        QuickConverter.EquationTokenizer.AddNamespace(typeof(object));
        QuickConverter.EquationTokenizer.AddNamespace(typeof(System.Windows.Visibility));
    }
}

Here I also registered the System assembly (using “typeof(object)”) in order to make the primitive types (like bool) available.

2. Add the QuickConverter namespace to your Xaml files

As with all controls in xaml, before you can use a you a control you must create a reference to the namespace that the control is in. So to be able to access and use QuickConverter in your xaml file, you must include it’s namespace, which can be done using:

xmlns:qc="clr-namespace:QuickConverter;assembly=QuickConverter"

So should I go delete all my existing converters?

As crazy awesome as QuickConverter is, it’s not a complete replacement for converters. Here are a few scenarios where you would likely want to stick with traditional converters:

  1. You need some very complex logic that is simply easier to write using a traditional converter. For example, we have some converters that access our application cache and lock resources and do a lot of other logic, where it would be tough (impossible?) to write all of that logic inline with QuickConverter. Also, by writing it using the traditional approach you get things like VS intellisense and compile-time error checking.
  2. If the converter logic that you are writing is very complex, you may want it enclosed in a converter class to make it more easily reusable; this allows for a single reusable object and avoids copy-pasting complex logic all over the place. Perhaps the first time you write it you might do it as a QuickConverter, but if you find yourself copy-pasting that complex logic a lot, move it into a traditional converter.
  3. If you need to debug your converter, that can’t be done with QuickConverter (yet?).

Summary

So QuickConverter is super useful and can help speed up development time by allowing most, if not all, of your converters to be written inline. In my experience 95% of converters are doing very simple things (null checks, to strings, adapting one value type to another, etc.), which are easy to implement inline. This means fewer files and classes cluttering up your projects. If you need to do complex logic or debug your converters though, then you may want to use traditional converters for those few cases.

So, writing C# inline in your xaml; how cool is that! I can’t believe Microsoft didn’t think of and implement this. One of the hardest things to believe is that Johannes Moersch came up with this idea and implemented it while on a co-op work term in my office! A CO-OP STUDENT WROTE QUICKCONVERTER! Obviously Johannes is a very smart guy, and he’s no longer a co-op student; he’ll be finishing up his bachelor’s degree in the coming months.

I hope you find QuickConverter as helpful as I have, and if you have any suggestions for improvements, be sure to leave Johannes a comment on the CodePlex page.

Happy coding!

Comments

Johannes Moersch

Great write up Dan!

I have a couple things to add. First, there is also a markup extension named qc:QuickConverter that can be used to fill out a converter property. I find I actually prefer it to using qc:Binding. For example, you could create a standard binding and set the converter using this markup. This is particularly useful if you create any of your own markup extensions that want to make use of converters. I’ve actually found myself creating markup for setting filters on lists and using QuickConverter to write the filter logic in the Xaml.

Second, qc:Binding, qc:MultiBinding, and qc:QuickConverter all have properties named V0-V9. These properties are statically set using traditional markup. They can then be accessed as $V0-$V9. This allows you to use other markup like StaticResource (or anything custom that you write).

Debugging the actual logic you write is unfortunately not likely to happen. I generate the converters using expression trees, which can not be debugged in that way. I only way this may be possible is if I generated an equivalent method in traditional code that you could step through. This would dramatically increase load times (although they may still be negligible). Maybe I could make this a run time switch. Maybe for QuickConverter 2.0 (if that ever happens).

Thanks for writing this up! Johannes Moersch

Alex

The Visiblity converter did not work for me,

Failed to tokenize expression “$P ? Visibility.Visible : Visibility.Collapsed”. Did you forget a ‘$’?

deadlydog

@Alex There’s likely just something small that you are missing. I would suggest creating an Issue on the CodePlex site and Johannes can likely determine what the problem is pretty quickly. https://quickconverter.codeplex.com/workitem/list/basic

Johannes Moersch

Are you getting this error in the xaml designer or at runtime? I ask because the designer tends to throw a lot of errors when using QuickConverter (even though everything is fine at runtime). If this is a runtime issue, it would be great if you could create an issue at my codeplex site with some addition details. Thanks.

deadlydog

@Jerry Nixon I haven’t done any formal performance comparisons, but I also have not observed any performance degradation either while using apps that use QuickConverter. So if there is a performance impact, I suspect it’s near negligible.

Michael Shpilt

This is great stuff!

I added the NuGet to our application and it already saved me writing a new unnecessary converter.

I wasn’t able to use some signs in XAML like the ‘<' sign and the '&' sign but I was able to work around those.

Way to go Johannes!

Johannes Moersch

You can use either ## or && for AND, but ## is preferred because ampersand is a special character in XAML.

Paul Pethoe

I can not use string literals in the expression part! i.e.: {qc:Binding ‘$P ? "some string" : ‘"another string"’, P={Binding SomeBool}}

Any workaround? (For example: expression part in XAML element somewhere? Of course XAML extension can only be used in XML attrib :()

Paul Pethoe

I have found a work around: I should set up bindig parameter with Source: P={Binding Source=someString, Mode=OneWay}}

Johannes Moersch

Hey Paul, there are a couple solutions to this. First, you can use string literals if you use single quotes and escape them first. For example ‘$P ? 'One' : 'Two'’. The second option is to set the V0 property and use it. For example {qc:QuickConverter ‘$P ? $V0 : $V1’, V0=’One’, V1=’Two’}. You can find some more examples here: https://github.com/JohannesMoersch/QuickConverter/blob/master/README.md

Leave a Comment

Your email address will not be published. Required fields are marked *

Loading...