引言
Avalonia UI 中有一个有趣的 FuncValueConverter
,它允许我们直接在代码后台简单地声明一个值转换器,而不需要额外写一个类。它地源代码可以在 GitHub 上看到。我们可以仿照这个实现一个类似的值转换器。
FuncValueConverter<TIn,TOut>
public sealed class FuncValueConverter<TIn, TOut> : IValueConverter
{
private readonly Func<TIn, TOut> _convert;
public FuncValueConverter(Func<TIn, TOut> convert)
{
_convert = convert;
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is TIn t)
{
return _convert(t);
}
return Binding.DoNothing;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return Binding.DoNothing;
}
}
对于 Convert
方法的实现,这里还有一种更好的方式。我们都知道,在 XAML 书写的很多资源,WPF 都会在底层帮我们进行合适的类型转换。比如我们将 "1"
字符串赋值给一个 int
类型的属性,WPF 会自动将其转换成 1
;我们将 "Visible"
字符串赋值给一个 Visibility
枚举类型的属性,WPF 也会进行相应的转换。如果我们不提供这个功能,那么我们写的这个 FuncValueConverter
就会变得不够灵活。因此,我们可以借助 .NET 原生的 TypeDescriptor
类来实现这个功能。
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is not TIn t)
{
if (value is null)
{
return default(TOut);
}
if (TypeDescriptor.GetConverter(typeof(TIn)).CanConvertFrom(value.GetType()))
{
t = (TIn)TypeDescriptor.GetConverter(typeof(TIn)).ConvertFrom(value);
}
else
{
return Binding.DoNothing;
}
}
return _convert(t);
}
这样我们就可以声明并使用了。我们需要将它声明为静态属性:
public class MainViewModel : ViewModelBase
{
public static FuncValueConverter<string, int> StringToIntConverter { get; } = new(s => int.Parse(s));
}
然后我们就可以在 XAML 中这样使用:
<StackPanel>
<TextBox x:Name="textBox" Text="123"/>
<TextBlock Text="{Binding ElementName=textBox, Path=Text, Converter={x:Static local:MainViewModel.StringToIntConverter}}"/>
</StackPanel>