Simplifying Converters for WPF

Total: 1 Average: 3

I have been working with WPF for about a year and some things annoying me very much. One of such things is converters. Why do I need to declare the implementation of a dubiously looking interface somewhere at the deep of a project and then search for it through CTRL + F by name when it is needed?

Well, it’s time to make a little easier on the routine of creating and using the converters.

I would like to note that I do not mind using converters such as BooleanToVisibilityConverter. We can keep them in mind and use in different areas. However, often it happens that it is required to use a particular converter and you do not want to make the whole component of it because it takes much time, as well as clogs the global scope. Thus, it is difficult to search the required information from all the garbage.

Converters are used when working with bindings. They allow converting values using one-way or two-way binding modes. There are one and multi-value converters: IValueConverter and IMultiValueConverter.

With a single value, we use standard bindings via the BindingBase markup extension available in XAML:

With multiple values, the following humongous construction is used:

The converters themselves will look like this. In this code, we have two converters in one class. However, we can use them separately:

The code is short, as the example is synthetic. However, it is difficult to understand what are these arrays, casting, targetType, and culture? What does ConvertBack without implementation mean?

Simplification methods

I have several ideas how we can simplify the process:

  1. Converters as C# snippets in XAML for simple calculations. See an example.
  2. Converters as references in code-behind methods for cases with particular conversions.
  3. Converters are used in their standard implementation.mine a conversion method in code-behind. I think it should look like this:
Compare his implementation with the previous one. The less code is, the more clarity there is. The variant with a separate re-used converter may look similar:
Not bad at all, is it? So, how can we work with XAML if it understands only standard interfaces of converters? Surely, we can wrap each class in IValueConverter/IMultiValueConverter, which will use beautiful methods. However, then there is no sense in declaring each readable converter by a wrapper. One of the solutions is to make the wrapper unique, such as:
This all is a theory. How can we pass delegates to converters in practical terms and how can we take them with XAML?

To do this, we can use the MarkupExtension mechanism. Just inherit the MarkupExtension class and override the ProvideValue method. Later in the XAML, you can write binding-similar expressions in figure brackets, but with their own working mechanisms.

The simplest solution to pass a reference to conversion methods using markup extensions is to use their string names. Let’s agree that in code-behind we will simply define the method name, while in external libraries we will have static methods of the ClrNamespace.ClassName.MethodName type. You can distinguish them by a dot of the latter one.

We have already analyzed how it is possible to identify methods. So, how can we get them in the markup extension as delegates to pass to the converter?

MarkupExtension has the ProvideValue method to override, which is as follows:

The overridden method must return what is eventually assigned to the property. The value of this property in XAML markup defines this markup extension.

The method can return any value, but because we are going to pass this markup extension to the Сonverter property, the return value must be a converter that is an instance of the IValueConverter/IMultiValueConverter type.

There is no sense in creating different converters. Instead, we can create one class and implement both interfaces for the converter to be suitable for one-value and multi-value binding.

To pass a string to the markup extension that specifies the function name from the code-behind or static library to be called by the converter, you must define the public string property in the MarkupExtension instance:

In the markup, you can write the following code:

However, we can simplify it as well.  It is not necessary to add “Extension” to the extension class name -conv:GenericConvExtension. Just leave it as conv:GenericConv.

You can then define the constructor in the extension so that you do not explicitly specify the property name with the function name:

Now, the statement in XAML looks simpler:

Note that there are no quotation marks in the name of the conversion function. In the cases when there are no spaces or other symbols in the string, single quotes are not used.

Now all you have to do is get a reference to the method in the ProvideValue method, create an instance of the converter, and pass this reference to it.

A reference to the method can be obtained through the reflection mechanism. To do this, you must know the runtime type in which this method is declared.  In the case of the implementation of conversion methods in static classes, the full name of the static method is passed. You can parse this string, using the full type name through reflection, and then get the method definition as a MethodInfo instance.

In the case of a code behind, we need not only a type but an instance of that type (the method may not be static and the state of the window instance should be considered when the conversion result is issued). Fortunately, this is not a problem because it can be obtained through the input parameter of the ProvideValue method:

Rootobject is the object in which code-behind is written. In the case of a window, it will be the Window object. By invoking GetType, you can get the conversion method we’re interested in by Reflexing, because its name is set in a previously defined FunctionNname property. Next, you simply create an instance of Genericconverter, passing the resulting methodinfo to it and returning the converter as a result of ProvideValue.

Implementation in  XAML

In the end of the article, I will provide my own implementation. The implementation in the string with the method name accepts both conversion method and optionally method of backward conversion. The syntax is as follows:

My code will be implemented in XAML in the following way:

I found the following downsides in my own implementation:

  1. At the time the method is called, various checks occur, and arrays are created for the so parameters. I’m not sure that MethodInfo. Invoke works as quickly as calling a method directly, but I wouldn’t say it’s a big minus in the context of working with WPF/MVVM.
  2. There is no way to use overloads. At the time the MethodInfo is received, the value types will not be known, so the required method overload cannot be retrieved at this time.
  3. In multi-value bindings, it is not possible to create different behavior of the converter depending on the number of passed parameters. If a conversion function is determined for 3 parameters, then the number of multi-value bindings should be the same, while in the standard converter you can create a various number.

Source code:

Thank you for your attention!

Ilya Mikhalin

Ilya Mikhalin is a .NET developer