AvaloniaUI / Avalonia

Develop Desktop, Embedded, Mobile and WebAssembly apps with C# and XAML. The most popular .NET UI client technology
https://avaloniaui.net
MIT License
26.05k stars 2.25k forks source link

Register Default TypeConverters through .net mechanism #12194

Open tisis2 opened 1 year ago

tisis2 commented 1 year ago

Hi there,

Is your feature request related to a problem? Please describe. as far as i see there is no way to find the default converters for IImage or FontFamily and others during runtime. in WPF all type convertions are registered through the TypeConverterAttribute. that leads to the fact that you can find the converter also during runtime. e.g. in the past i created a converter for WPF that would format a string with bound values and should be used as it would have been a input from xaml. since the type converters are not used when using bindings i created a converter that would do string format and convert to target type. for reference here:

public class StringFormatToTargetTypeConverter : IValueConverter, IMultiValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return Convert(new object[] { value }, targetType, parameter, culture);
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    [Localizable(false)]
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        if (parameter is null)
        {
            throw new ArgumentException("string format set as parameter");
        }

        var typeConverter = TypeDescriptor.GetConverter(targetType);
        if (typeConverter is null)
        {
            throw new ArgumentException($"no TypeConverter for target type found: {targetType.FullName}");
        }

        if (!typeConverter.CanConvertFrom(typeof(string)))
        {
            throw new ArgumentException($"TypeConverter '{typeConverter.GetType().FullName}' cannot convert from string");
        }

        var formatted = string.Format(parameter.ToString()!, values);
        return typeConverter.ConvertFrom(formatted);
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

this is not possible in Avalonia since TypeDescriptor.GetConverter(targetType) would not always find the converter (e.g. BitmapTypeConverter for IImage). instead the type convertion for these type are done in the xaml loader here https://github.com/AvaloniaUI/Avalonia/blob/master/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguage.cs#L128. this makes it hard to write very generic converters like the above example. it would be needed to copy this mapping over to your project and resolve the correct converter on your own. what could later on lead to incompatibilities on future update when the list or logic changes.

Describe the solution you'd like use the typeconvertion mechanism of .net or provide a way to fetch the converter used for a type

maybe im missing anything or there is even another way to reach what i am looking for and you could point me to that. thanks for your help

maxkatz6 commented 1 year ago

Correct, in Avalonia we either skip type converters at all, injecting type parsing/casting via a compiler or something like hardcoding these converters in the compiler like BitmapTypeConverter.

In general problem with TypeDescriptor is that it often relies on reflection.

I think we have two options:

  1. Add type converter on all of these missing members (there is more of them, see what we handle compile time without converters: https://github.com/AvaloniaUI/Avalonia/blob/master/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguageParseIntrinsics.cs)
  2. Or make sure that DefaultValueConverter works as a replacement. It should already work for most types, but not sure about bitmaps (these could be a special case as well in your converter).
fitdev commented 1 year ago

@maxkatz6

Sorry to hijack the issue, but just to clarify...

If I have a custom type MyCustomColorType, and I want its value to be bindable in XAML in places where say Avalonia's IBrush is expected like <Border Background="{Binding ColorValueAsMyCustomColorType}" />, currently there is no way to do so unless a Converter is also specified with every binding in XAML markup?

I.e. there is no way to somehow "register" with Avalonia the notion that MyCustomColorType is convertible to IBrush via a given function / IValueConverter / TypeConverter / some other mechanism?

Is this a limitation of compiled bindings or all bindings (including reflection bindings)?

If currently it is indeed not possible, then perhaps some helper facility can be introduced in Avalonia: