How to Bind Multiple Properties
MultiBinding
In scenarios where a target property must be assigned a result calculated from several other bound properties, a
MultiBinding may be the appropriate solution. MultiBinding aggregates multiple Binding objects and produces a result
through the use of an IMultiValueConverter. The Convert method is called each time any of the bound properties notify
of change. Similar to Binding, MultiBinding can be used to bind properties on ViewModels, Controls, or other sources.
MultiBinding only supports BindingMode.OneTime and BindingMode.OneWay.
IMultiValueConverter
Similar to IValueConverter in that it defines conversions to a target property. There is no ConvertBack
method as aggregate operations are irreversible.
public interface IMultiValueConverter
{
object? Convert(IList<object?> values, Type targetType, object? parameter, CultureInfo culture);
}
MultiBinding Example
Consider the following scenario where you have inputs for red, green, and blue color channels. The aim is to bind
all 3 inputs and provide an IBrush for another control to draw with. Below, the color channel values are constrained
to the proper range ([0, 255]) by the NumericUpDown. Creating <Binding> objects is necessary as the Binding
MarkupExtension cannot be used because there aren't properties to target.
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" Spacing="8">
<NumericUpDown x:Name="red" Minimum="0" Maximum="255" Increment="20" Value="0" Foreground="Red" />
<NumericUpDown x:Name="green" Minimum="0" Maximum="255" Increment="20" Value="0" Foreground="Green" />
<NumericUpDown x:Name="blue" Minimum="0" Maximum="255" Increment="20" Value="0" Foreground="Blue" />
<TextBlock Text="MultiBinding Text Color!" FontSize="24">
<TextBlock.Foreground>
<MultiBinding Converter="{StaticResource RgbToBrushMultiConverter}">
<Binding Path="Value" ElementName="red" />
<Binding Path="Value" ElementName="green" />
<Binding Path="Value" ElementName="blue" />
</MultiBinding>
</TextBlock.Foreground>
</TextBlock>
</StackPanel>
Next, we create the IMultiValueConverter. Type checking of parameters is important. In this scenario, NumericUpDown.Value
is a decimal? so both decimal and null must be checked. The value may also be UnsetValueType when the bindings are
being initialized. Further numeric conversion could be done to make the converter broadly compatible with numeric types.
public sealed class RgbToBrushMultiConverter : IMultiValueConverter
{
public object? Convert(IList<object?> values, Type targetType, object? parameter, CultureInfo culture)
{
// Ensure all bindings are provided and attached to correct target type
if (values?.Count != 3 || !targetType.IsAssignableFrom(typeof(ImmutableSolidColorBrush)))
throw new NotSupportedException();
// Ensure all bindings are correct type
if (!values.All(x => x is decimal or UnsetValueType or null))
throw new NotSupportedException();
// Pull values, DoNothing if any are unset.
// Convert is called several times during initialization of bindings,
// so some properties will be initially unset.
if (values[0] is not decimal r ||
values[1] is not decimal g ||
values[2] is not decimal b)
return BindingOperations.DoNothing;
byte a = 255;
var color = new Color(a, (byte)r, (byte)g, (byte)b);
return new ImmutableSolidColorBrush(color);
}
}
- Consider creating a
MarkupExtensionto simplify the XAML syntax when aMultiBindingis frequently reused. - Consider using
FuncMultiValueConverterto reduce the amount of code needed for simpler converters.