WPF:如何在 1 个绑定中使用 2 个转换器?

Posted

技术标签:

【中文标题】WPF:如何在 1 个绑定中使用 2 个转换器?【英文标题】:WPF: how to use 2 converters in 1 binding? 【发布时间】:2010-12-08 08:07:09 【问题描述】:

我有一个要显示/隐藏的控件,具体取决于布尔值。

我有一个NegatedBooleanConverter(将 true 切换为 false,反之亦然),我需要先运行此转换器。 我有一个BooleanToVisibilityConverter,我需要在NegatedBoolConverter 之后运行这个转换器。

我该如何解决这个问题?我想在 XAML 中执行此操作。

编辑:this is a possible solution.

这似乎不起作用。它首先使用单独的转换器转换值,然后对转换后的值进行处理。

我需要的是:

用第一个转换器转换值(这给出了convertedValue)。 用第二个转换器转换convertedValue,这就是我需要的结果。

【问题讨论】:

是的,您链接到的解决方案可能是最好的... 另见Chaining multiple converters in XAML Town 说得最好here,他的解决方案让你可以链接任意数量,类似于 jberger 的答案,但这个更优雅,实现更短 【参考方案1】:

在这一点上,我想推荐ValueConverters.NET (NuGet),它有大量有用的ValueConverters,包括可用于组合ValueConverters 的ValueConverterGroup

BoolToValueConverters 还提供定义TrueValueFalseValue 以及输入IsInverted 的字段,因此在大多数情况下甚至不需要ValueConverterGroup

只是为了说明生活可以变得多么容易,这里有一个示例演示,它显示了一个转换器,如果绑定不为空,则显示一个元素:

<Window ...
        xmlns:vc="clr-namespace:ValueConverters;assembly=ValueConverters"
        ...>
...
    <vc:ValueConverterGroup x:Key="IsNotNullToVisibilityConverter">
      <vc:NullToBoolConverter IsInverted="True" />
      <vc:BoolToVisibilityConverter />
    </vc:ValueConverterGroup>

ValueConverters 是在许多 WPF 应用程序中重新发明***的典型例子。为什么必须这样?

通常也可以使用StyleTriggers 或ViewModels 逻辑本身来解决复杂的问题。

我几乎从未需要构建自定义转换器。在我看来,WPF 已经有足够的工程师需求了。

【讨论】:

【参考方案2】:

这就是我所做的:

public class CombiningConverter : IValueConverter

    public IValueConverter Converter1  get; set; 
    public IValueConverter Converter2  get; set; 

    public object Convert(
        object value, Type targetType, object parameter, CultureInfo culture)
    
        object convertedValue =
            Converter1.Convert(value, targetType, parameter, culture);
        return Converter2.Convert(
            convertedValue, targetType, parameter, culture);
    

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

我这样称呼它:

<converters:CombiningConverter
    x:Key="negatedBoolToVisibilityConverter"
    Converter1="StaticResource NegatedBooleanConverter"
    Converter2="StaticResource BoolToVisibilityConverter" />

我认为MultiValueConverter 也可能是可能的。也许我稍后会尝试。

【讨论】:

【参考方案3】:

以下是Natrium 和metao 的组合答案,可为您节省一些时间:

public class ComparisonConverter : IValueConverter

    public object TrueValue  get; set;  = true;
    public object FalseValue  get; set;  = false;


    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    
        return value?.Equals(parameter) == true? TrueValue : FalseValue;
    

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    
        return value?.Equals(TrueValue) == true ? parameter : Binding.DoNothing;
    

以及你如何使用它:

<converter:ComparisonConverter x:Key="ComparisonConverter" />
<converter:ComparisonConverter TrueValue="x:Static Visibility.Visible"
                               FalseValue="x:Static Visibility.Collapsed"
                               x:Key="ComparisonToVisibilityConverter" />
...

<RadioButton IsChecked="Binding Type, ConverterParameter=x:Static entities:LimitType.MinMax, Converter=StaticResource ComparisonConverter"/>
<TextBox Visibility="Binding Type, ConverterParameter=x:Static entities:LimitType.MinMax, Converter=StaticResource ComparisonToVisibilityConverter"/>

【讨论】:

【参考方案4】:

我刚刚创建了我称之为 ReversedBooleanToVisibilityConverter 的东西,基本上可以完成这 2 个操作,但只需一步完成。

【讨论】:

是的,这将是一个可能的解决方案,但我更喜欢这样的解决方案,即我可以重复使用当前的转换器,而无需重新发明***。如果存在该解决方案...【参考方案5】:

为了解决这个特定问题,您可以编写自己的BoolToVisibilityConverter,而不是使用两个转换器,它使用ConverterParameter(作为bool)来确定是否否定原始布尔值。

【讨论】:

是的,这将是一个可能的解决方案,但我更喜欢这样的解决方案,即我可以重复使用当前的转换器而无需重新发明***。如果该解决方案存在...【参考方案6】:

再次回答我自己的问题:多年来我一直在使用这个解决方案:

Piping Value Converters in WPF - CodeProject

它使用 2 个现有转换器创建一个新转换器,首先调用第一个,然后调用第二个,依此类推。

我对这个解决方案很满意。

【讨论】:

【参考方案7】:

我们在项目中所做的是制作一个常规的BooleanToVisibilityConverter,表示转换器采用一个参数(任何参数,stringintbool,等等)。如果设置了参数,它会反转结果,如果没有,它会吐出常规结果。

public class BooleanToVisibilityConverter : IValueConverter

    #region IValueConverter Members

    public object Convert(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
    
        bool? isVisible = value as bool?;
        if (parameter != null && isVisible.HasValue)
            isVisible = !isVisible;
        if (isVisible.HasValue && isVisible.Value == true)
            return Visibility.Visible;
        else
            return Visibility.Collapsed;
    

    public object ConvertBack(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
    
        throw new System.NotImplementedException();
    

    #endregion

【讨论】:

如何在xaml中调用这个转换器?【参考方案8】:

在这种情况下,您不需要转换器链。你只需要一个可配置的转换器。这与上面 Carlo 的回答类似,但明确定义了 true 和 false 值(这意味着您可以对 HiddenVisibleCollapsed 转换使用相同的转换器)。

[ValueConversion(typeof(bool), typeof(Visibility))]
public class BoolToVisibilityConverter : IValueConverter

    public Visibility TrueValue  get; set; 
    public Visibility FalseValue  get; set; 

    public BoolToVisibilityConverter()
    
        // set defaults
        FalseValue = Visibility.Hidden;
        TrueValue = Visibility.Visible;
    

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    
        return (bool)value ? TrueValue : FalseValue;
    

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

然后在 XAML 中:

<BoolToVisibilityConverter x:Key="BoolToVisibleConverter"
                           FalseValue="Hidden"
                           TrueValue="Visible" />

【讨论】:

如果您可以使用 XAML 2009,您也可以将其设为通用。属性语法:Converter=c:BooleanConverter(Visibility) True=Visible, False=Collapsed 元素语法:&lt;c:BooleanConverter x:TypeArguments="FontWeight" True="Bold" False="Normal" /&gt; 如果您不能使用 XAML 2009,您仍然可以将基类设为泛型,但您需要为每个泛型类型提供具体的派生类。【参考方案9】:

扩展 Natrium 的最佳答案...

XAML

<conv:ConverterChain x:Key="convBoolToInverseToVisibility">
    <conv:BoolToInverseConverter />
    <BooleanToVisibilityConverter />
</conv:ConverterChain>

/// <summary>Represents a chain of <see cref="IValueConverter"/>s to be executed in succession.</summary>
[ContentProperty("Converters")]
[ContentWrapper(typeof(ValueConverterCollection))]
public class ConverterChain : IValueConverter

    private readonly ValueConverterCollection _converters= new ValueConverterCollection();

    /// <summary>Gets the converters to execute.</summary>
    public ValueConverterCollection Converters
    
        get  return _converters; 
    

    #region IValueConverter Members

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    
        return Converters
            .Aggregate(value, (current, converter) => converter.Convert(current, targetType, parameter, culture));
    

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    
        return Converters
            .Reverse()
            .Aggregate(value, (current, converter) => converter.Convert(current, targetType, parameter, culture));
    

    #endregion


/// <summary>Represents a collection of <see cref="IValueConverter"/>s.</summary>
public sealed class ValueConverterCollection : Collection<IValueConverter>  

【讨论】:

这个article 的解决方案提供了运行时类型检查。 这会将错误的 targetType 传递给转换器。它将目标 targetType 传递给所有转换器,即使在开放问题的场景中也是不正确的。当 targetType 实际上是“bool”时,“Visibility”的类型被传递给第一个转换器。 @JakeBerger,我认为你应该使用 ConvertBack 到 ConverBack 函数?【参考方案10】:

我认为您可能希望在此处使用 Multiconverter 而不是两个单独的转换器。您应该能够重用现有转换器的逻辑。查看this discussion 开始。

【讨论】:

【参考方案11】:

就我个人而言,我只会制作 1 个进行完整转换的单个转换器。除非您在其他地方迫切需要转换器(如否定),否则如果在一个地方完成一次转换,则维护(imo)会更容易。

【讨论】:

是的,这将是一个可能的解决方案,但我更喜欢这样的解决方案,即我可以重复使用当前的转换器而无需重新发明***。如果该解决方案存在...

以上是关于WPF:如何在 1 个绑定中使用 2 个转换器?的主要内容,如果未能解决你的问题,请参考以下文章

WPF Radiobutton(二)(绑定到布尔值)

如何在 WPF 中使用多个 ViewModel 并通过一个 MainViewModel 绑定它们?

WPF绑定并转换

如何在双向绑定的Image控件上绘制自定义标记(wpf)

WPF ListView - 2 个维度,1 个相同的结构

WPF:如何使用 MVVM 将命令绑定到 ListBoxItem?