在UnsetValue中转换一个值时,使用convertback进行多重绑定不起作用

时间:2014-11-19 10:56:47

标签: c# wpf binding unset

我有以下Multibinding:

 <Grid.Visibility>
    <MultiBinding Converter="{StaticResource MyMultiValueConverter}" Mode="TwoWay">
      <Binding RelativeSource="{RelativeSource TemplatedParent}" Path="MyVisibilityDependencyProperty" Mode="TwoWay"/>
      <Binding RelativeSource="{RelativeSource TemplatedParent}" Path="MyBoolProperty" Mode="TwoWay"/>
   </MultiBinding>
</Grid.Visibility>

MyVisibilityDependencyProperty是一个依赖项属性。 MyBoolProperty是一个普通的属性。 MyMultiValueConverter的实现是重要的事情:

public class MyMultiValueConverter: IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
       //Not interesting
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        return new[] { value, Binding.DoNothing};
    }
}

现在的情景:我确实这么做了。触发ConvertBack-Method的调用,这意味着我在那里遇到了一个断点。之后我在MyVisibilityDependencyProperty的OnPropertyChangedCallback中找到了一个断点。在那里我可以看到MyVisibilityDependencyProperty的新值是ConvertBack-Method中设置的值。

现在我不明白的问题。我将ConvertBack-Method的实现更改为:

public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        return new[] { value, DependencyProperty.UnsetValue};
    }

现在我遵循完全相同的情况。我做的很好。触发ConvertBack-Method的调用,这意味着我在那里遇到了一个断点。之后没有任何反应。未调用OnPropertyChangedCallback,并且不更新MyVisibilityDependencyProperty。为什么呢?

似乎如果数组中的一个值是DependencyProperty.UnsetValue,则停止所有值的传播。不仅是该值,还包括数组中的所有值。这受以下行为支持:

return new[] { Binding.DoNothing, false };

这导致调用MyBoolProperty的setter。

return new[] { DependencyProperty.UnsetValue, false };

这不会调用MyBoolProperty的setter。

我在文档中找不到任何解释的暗示,在我看来这没有意义。

4 个答案:

答案 0 :(得分:8)

  

我在文档中找不到任何解释提示,但在我看来这没有任何意义。

我不记得曾在文档中看过它,但您的观察是正确的:

如果IMultiValueConverter.ConvertBack的结果中的任何元素为UnsetValue,则整套建议值将被拒绝,即转化失败,并且没有任何子绑定更新其源值。

相关代码可在MultiBindingExpression课程中找到。以下是摘录摘录。

internal override object ConvertProposedValue(object value)
{
    object result;
    bool success = ConvertProposedValueImpl(value, out result);
    {
        result = DependencyProperty.UnsetValue;
        ...
    }
    return result;
}

private bool ConvertProposedValueImpl(object value, out object result)
{
    result = GetValuesForChildBindings(value);

    object[] values = (object[])result;
    int count = MutableBindingExpressions.Count;
    bool success = true;

    // use the smaller count
    if (values.Length < count)
        count = values.Length;

    for (int i = 0; i < count; ++i)
    {
        value = values[i];
        ...
        if (value == DependencyProperty.UnsetValue)
            success = false; // if any element is UnsetValue, conversion fails
        values[i] = value;
    }

    result = values;
    return success;
}

至于它是否有意义,我认为确实如此。结果数组中的值DoNothing表示应跳过相应的子绑定,即不应更新其源值。实际上,这提供了部分更新的机制。如果您考虑一下,我们关心的唯一场景是:

  1. 所有来源均已成功更新
  2. 某些来源已成功更新
  3. 完全失败:没有更新来源
  4. 正常行为提供(1),DoNothing的使用可以满足(2)。使用UnsetValue表示完全失败是有道理的。这也与单值转换器的含义一致:UnsetValue表示转换失败。唯一的区别是ConvertBack会返回object[],因此您无法直接返回UnsetValue。但是,您可以返回一个仅包含 UnsetValue的数组:由于它的存在意味着整个结果被抛出,因此数组长度实际上不必与子绑定的数量相匹配。

答案 1 :(得分:2)

前段时间我有一个类似的问题[Demultiplexing using IMultiValueConverter]

对我有用的是“延迟”DependencyProperty.UnsetValue的分配:由于MultiBinding有点是Bindings的“集合”,因此您可以为每个参与者分配IValueConverter 1 -1绑定如下:

(1)XAML-Markup:为所涉及的1:1绑定引入转换器

<Grid.Visibility>
    <MultiBinding Converter="{StaticResource MyMultiValueConverter}" Mode="TwoWay">
        <Binding RelativeSource="{RelativeSource TemplatedParent}" Path="MyVisibilityDependencyProperty" Mode="TwoWay" />
        <Binding Converter="UnsetValueConverter" RelativeSource="{RelativeSource TemplatedParent}" Path="MyBoolProperty" Mode="TwoWay"/>
    </MultiBinding>
</Grid.Visibility>

(注意第二个绑定中引入的Converter="UnsetValueConverter"

(2)实现MyMultiValueConverter:创建所提供元素的副本

public class MyMultiValueConverter: IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
       //Not interesting
    }

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

(3)实现in(1)引入的UnsetValueConverter

public class UnsetValueConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return value;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return DependencyProperty.UnsetValue;
    }
}

这样处理会延迟到更新值发布之前

是的,这不是你问题的答案,但Mike S.在解释它方面做得很好,并尝试提供通用的解决方案

答案 2 :(得分:1)

根据MSDN:

  

UnsetValue是一个标记值,用于场景,其中WPF属性系统无法确定请求的DependencyProperty值。使用UnsetValue而不是null,因为null可以是有效的属性值,也可以是有效的(经常使用的)DefaultValue。

事实上,您无法将DependencyProperty设置为UnsetValue,您可以将其与之进行比较。 设置为UnsetValue没有效果,您可以自己试试。

答案 3 :(得分:-3)

试试这个:

将源值转换为绑定目标的值。数据绑定引擎在将值从源绑定传播到绑定目​​标时调用此方法。

    ///param name="values"> The array of values taht the source bindings in the<see cref="T:System.Windows.Data.MultiBinding"/>produces.The value <see cref="F:System.Windows.DependencyProperty.UnsetValue"/> indicates that the source binding has no value to provide for conversion.</param>
    /// <param name="targetType">The type of the binding target property.</param>
    /// <param name="parameter">The converter parameter to use.</param>
    /// <param name="culture">The culture to use in the converter.</param>
    /// <returns>
    /// A converted value.
    /// If the method returns null, the valid null value is used.
    /// A return value of <see cref="T:System.Windows.DependencyProperty"/>.<see cref="F:System.Windows.DependencyProperty.UnsetValue"/> indicates that the converter did not produce a value, and that the binding will use the <see cref="P:System.Windows.Data.BindingBase.FallbackValue"/> if it is available, or else will use the default value.
    /// A return value of <see cref="T:System.Windows.Data.Binding"/>.<see cref="F:System.Windows.Data.Binding.DoNothing"/> indicates that the binding does not transfer the value or use the <see cref="P:System.Windows.Data.BindingBase.FallbackValue"/> or the default value.
    /// </returns>

public object ConvertBack( object[] values, Type targetType, object parameter, System.Globalization.CultureInfo clture )
    {
        if( parameter == null )
           { return null;}


        return String.Format( parameter.ToString(), values );

    }
相关问题