How to Create a Custom Data Binding Converter
When one of the built-in data binding converters does not meet your conversion requirements, you can write a custom converter based on the IValueConverter
interface. This guide will show you how.
To review the Microsoft documentation for the IValueConverter
interface, see here.
As the IValueConverter
interface was not available in .NET standard 2.0, Avalonia UI contains a copy in the Avalonia.Data.Converters
namespace. You can see the API documentation about this interface, here.
You must reference a custom converter in some resources before it can be used. This can be at any level in your application. In this example, the custom converter myConverter
is referenced in the window resources:
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ExampleApp;assembly=ExampleApp">
<Window.Resources>
<local:MyConverter x:Key="myConverter"/>
</Window.Resources>
<TextBlock Text="{Binding Value, Converter={StaticResource myConverter}}"/>
</Window>
Example
This example data binding converter can convert text to specific case from a parameter:
<TextBlock Text="{Binding TheContent,
Converter={StaticResource textCaseConverter},
ConverterParameter=lower}" />
The above XAML assumes that the textCaseConverter
has been referenced in a resource.
public class TextCaseConverter : IValueConverter
{
public static readonly TextCaseConverter Instance = new();
public object? Convert(object? value, Type targetType, object? parameter,
CultureInfo culture)
{
if (value is string sourceText && parameter is string targetCase
&& targetType.IsAssignableTo(typeof(string)))
{
switch (targetCase)
{
case "upper":
case "SQL":
return sourceText.ToUpper();
case "lower":
return sourceText.ToLower();
case "title": // Every First Letter Uppercase
var txtinfo = new System.Globalization.CultureInfo("en-US",false)
.TextInfo;
return txtinfo.ToTitleCase(sourceText);
default:
// invalid option, return the exception below
break;
}
}
// converter used for the wrong type
return new BindingNotification(new InvalidCastException(),
BindingErrorType.Error);
}
public object ConvertBack(object? value, Type targetType,
object? parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
Target Property Type
You may want to write a a custom converter that can switch the output type depending on what the target property requires. You can achieve this becuase the Convert
method receives a targetType
argument that you can test with the IsAssignableTo
function.
In this example, the animalConverter
can find an image, or a text name for a bound Animal
class object:
<Image Width="42"
Source="{Binding Animal, Converter={StaticResource animalConverter}}"/>
<TextBlock
Text="{Binding Animal, Converter={StaticResource animalConverter}}" />
public class AnimalConverter : IValueConverter
{
public static readonly AnimalConverter Instance = new();
public object? Convert( object? value, Type targetType,
object? parameter, CultureInfo culture )
{
if (value is Animal animal)
{
if (targetType.IsAssignableTo(typeof(IImage)))
{
img = @"icons/generic-animal-placeholder.png"
switch (animal)
{
case Dog d:
img = d.IsGoodBoy ? @"icons/dog-happy.png"
: @"icons/dog.png";
break;
case Cat:
img = @"icons/cat.png";
break;
// etc. etc.
}
// see https://docs.avaloniaui.net/docs/guides/data-binding/how-to-create-a-custom-data-binding-converter
return BitmapAssetValueConverter.Instance
.Convert(img, typeof(Bitmap), parameter, culture);
}
else if (targetType.IsAssignableTo(typeof(string)))
{
return !string.IsNullOrEmpty(animal.NickName) ?
$"{animal.Name} \"{animal.NickName}\"" : animal.Name;
}
}
// converter used for the wrong type
return new BindingNotification(new InvalidCastException(),
BindingErrorType.Error);
}
public object ConvertBack( object? value, Type targetType,
object? parameter, CultureInfo culture )
{
throw new NotSupportedException();
}
}
More Information
For further guidance about how to bind images, see here.