I have the following 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 is a dependency property. MyBoolProperty is a normal property. The implementation of MyMultiValueConverter is the important thing:
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};
}
}
Now the scenario: I do smth. that triggers a call of the ConvertBack-Method, which means I hit a break point there. Afterwards I hit a break point in the OnPropertyChangedCallback of MyVisibilityDependencyProperty. There I can see that the new value of MyVisibilityDependencyProperty is the value that was set in the ConvertBack-Method.
Now the issue that I do not understand. I change the implementation of the ConvertBack-Method to:
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
return new[] { value, DependencyProperty.UnsetValue};
}
Now I follow the exact same scenario. I do smth. that triggers a call of the ConvertBack-Method, which means I hit a break point there. After that nothing happens. The OnPropertyChangedCallback is not called and MyVisibilityDependencyProperty is not updated. Why?
It seems like that if one of the values in the array is DependencyProperty.UnsetValue, propagation of all values is stopped. Not only for that value but all values in the array. This is supported by the following behavior:
return new[] { Binding.DoNothing, false };
This results in a call of the setter of MyBoolProperty.
return new[] { DependencyProperty.UnsetValue, false };
This does not call the setter of MyBoolProperty.
I could not find any hints of explanation in documentation and it does not make sense in my opinion.
I don't recall ever seeing it in the documentation, but your observations are correct:
If any element in the result of
IMultiValueConverter.ConvertBack
isUnsetValue
, the entire set of proposed values is rejected, i.e., the conversion fails, and the none of the child bindings have their source values updated.The relevant code can be found in the
MultiBindingExpression
class. Below is an abbreviated excerpt.As to whether it makes sense, I think that it does. A value of
DoNothing
in the result array indicates that the corresponding child binding should be skipped, i.e., its source value should not be updated. This, in effect, provides a mechanism for partial updates. If you think about it, the only scenarios we care about are:The normal behavior provides (1), and the use of
DoNothing
can satisfy (2). It arguably makes sense to useUnsetValue
to indicate a total failure. That is also consistent with its meaning for single-value converters:UnsetValue
means the conversion failed. The only difference is thatConvertBack
returnsobject[]
, so you cannot returnUnsetValue
directly. You can, however, return an array containing onlyUnsetValue
: since its presence means the entire result gets thrown out, the array length doesn't actually have to match the number of child bindings.