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, Control
s, 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
MarkupExtension
to simplify the XAML syntax when aMultiBinding
is frequently reused. - Consider using
FuncMultiValueConverter
to reduce the amount of code needed for simpler converters.