WPF:值转换器放在哪里?





5.00/5 (22投票s)
本文试图解决的问题是:
以下技巧并非新鲜事物;它基于这篇文章及其评论。但是,为了完整性和便于将来参考,我把最终版本放在这里。
本文试图解决的问题是:
常规方案
通常,放置值转换器的常用位置是在资源部分。有了这个,你可以使用 `StaticResource` 语法来使用值转换器。
<UserControl.Resources>
<local:NotConverter x:Key="notConverter" />
</UserControl.Resources>
...
<StackPanel>
<Button IsEnabled="{Binding Converter={StaticResource notConverter}}" />
</StackPanel>
这意味着每次你想使用值转换器时,都需要将其添加到资源部分。更好的方法是将其放在全局转换器资源文件中,这样资源定义只需进行一次。
巧妙方案
简而言之,让你的转换器继承自 `MarkupExtension`。这将完全避免这个问题。完整的解决方案提供了一个你应该从中继承的通用基类,`ConverterMarkupExtension`。从这个类继承的优点是:
- 你的转换器可以用作标记扩展。因此,之前的代码变成了:
<StackPanel> <Button IsEnabled="{Binding Converter={local:notConverter}}" /> </StackPanel>
具体来说,不需要任何资源定义。完全不需要。
- 只使用你的转换器的一个实例来提供值(类似单例)。
请注意,每次 XAML 编译器看到一个标记扩展时,它都会创建一个新实例,但是下面的实现总是返回相同的单个转换器。如果你担心标记扩展的所有短暂实例,只需考虑使用 `StaticResource`(另一个标记扩展)也会创建它们。因此,内存消耗没有额外的压力。
- 基类为 `IValueConverter` 和 `IMultiValueConverter` 方法提供了默认实现。它抛出 `NotImplementedException`。这看起来似乎意义不大,但在你只实现一个转换方向的常见情况下,这有助于缩短你的转换器代码。
ConverterMarkupExtension,为清晰起见简短版本
public abstract class ConverterMarkupExtension<T> : MarkupExtension, IValueConverter,
IMultiValueConverter
where T : class, new()
{
private static T _converter = null;
public override object ProvideValue(IServiceProvider serviceProvider)
{
if (_converter == null)
{
_converter = new T();
}
return _converter;
}
#region IValueConverter Members
public virtual object Convert(object value, Type targetType, object parameter,
CultureInfo culture)
{
throw new NotImplementedException();
}
public virtual object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
#region IMultiValueConverter Members
...
#endregion
这里,你可以获得包含大量注释的完整代码版本。
使用基类
以下是使用 `ConverterMarkupExtension` 实现你自己的转换器的示例。
using System;
using System.Windows.Data;
using System.Globalization;
namespace WPF.Common
{
/// <summary>
/// Returns the negation of the given boolean value
/// </summary>
public class NotConverter : ConverterMarkupExtension<NotConverter>
{
public override object Convert(object value, Type targetType, object parameter,
CultureInfo culture)
{
return !(bool)value;
}
}
}
请注意,值转换器代码实际上保持不变,但你可以将其用作标记扩展并删除“未实现”部分。
暂时就到这里,
Arik Poznanski。