Xamarin.Forms Multi-Bindings
Multi-bindings provide the ability to attach a collection of Binding
objects to a single binding target property. They are created with the MultiBinding
class, which evaluates all of its Binding
objects, and returns a single value through a IMultiValueConverter
instance provided by your application. In addition, MultiBinding
reevaluates all of its Binding
objects when any of the bound data changes.
The MultiBinding
class defines the following properties:
Bindings
, of typeIList<BindingBase>
, which represents the collection ofBinding
objects within theMultiBinding
instance.Converter
, of typeIMultiValueConverter
, which represents the converter to use to convert the source values to or from the target value.ConverterParameter
, of typeobject
, which represents an optional parameter to pass to theConverter
.
The Bindings
property is the content property of the MultiBinding
class, and therefore does not need to be explicitly set from XAML.
In addition, the MultiBinding
class inherits the following properties from the BindingBase
class:
FallbackValue
, of typeobject
, which represents the value to use when the multi-binding is unable to return a value.Mode
, of typeBindingMode
, which indicates the direction of the data flow of the multi-binding.StringFormat
, of typestring
, which specifies how to format the multi-binding result if it's displayed as a string.TargetNullValue
, of typeobject
, which represents the value that is used in the target wen the value of the source isnull
.
A MultiBinding
must use a IMultiValueConverter
to produce a value for the binding target, based on the value of the bindings in the Bindings
collection. For example, a Color
might be computed from red, blue, and green values, which can be values from the same or different binding source objects. When a value moves from the target to the sources, the target property value is translated to a set of values that are fed back into the bindings.
Important
Individual bindings in the Bindings
collection can have their own value converters.
The value of the Mode
property determines the functionality of the MultiBinding
, and is used as the binding mode for all the bindings in the collection unless an individual binding overrides the property. For example, if the Mode
property on a MultiBinding
object is set to TwoWay
, then all the bindings in the collection are considered TwoWay
unless you explicitly set a different Mode
value on one of the bindings.
Define a IMultiValueConverter
The IMultiValueConverter
interface enables custom logic to be applied to a MultiBinding
. To associate a converter with a MultiBinding
, create a class that implements the IMultiValueConverter
interface, and then implement the Convert
and ConvertBack
methods:
public class AllTrueMultiConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values == null || !targetType.IsAssignableFrom(typeof(bool)))
{
return false;
// Alternatively, return BindableProperty.UnsetValue to use the binding FallbackValue
}
foreach (var value in values)
{
if (!(value is bool b))
{
return false;
// Alternatively, return BindableProperty.UnsetValue to use the binding FallbackValue
}
else if (!b)
{
return false;
}
}
return true;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
if (!(value is bool b) || targetTypes.Any(t => !t.IsAssignableFrom(typeof(bool))))
{
// Return null to indicate conversion back is not possible
return null;
}
if (b)
{
return targetTypes.Select(t => (object)true).ToArray();
}
else
{
// Can't convert back from false because of ambiguity
return null;
}
}
}
The Convert
method converts source values to a value for the binding target. Xamarin.Forms calls this method when it propagates values from source bindings to the binding target. This method accepts four arguments:
values
, of typeobject[]
, is an array of values that the source bindings in theMultiBinding
produces.targetType
, of typeType
, is the type of the binding target property.parameter
, of typeobject
, is the converter parameter to use.culture
, of typeCultureInfo
, is the culture to use in the converter.
The Convert
method returns an object
that represents a converted value. This method should return:
BindableProperty.UnsetValue
to indicate that the converter did not produce a value, and that the binding will use theFallbackValue
.Binding.DoNothing
to instruct Xamarin.Forms not to perform any action. For example, to instruct Xamarin.Forms not to transfer a value to the binding target, or not to use theFallbackValue
.null
to indicate that the converter cannot perform the conversion, and that the binding will use theTargetNullValue
.
Important
A MultiBinding
that receives BindableProperty.UnsetValue
from a Convert
method must define its FallbackValue
property. Similarly, a MultiBinding
that receives null
from a Convert
method must define its TargetNullValue
propety.
The ConvertBack
method converts a binding target to the source binding values. This method accepts four arguments:
value
, of typeobject
, is the value that the binding target produces.targetTypes
, of typeType[]
, is the array of types to convert to. The array length indicates the number and types of values that are suggested for the method to return.parameter
, of typeobject
, is the converter parameter to use.culture
, of typeCultureInfo
, is the culture to use in the converter.
The ConvertBack
method returns an array of values, of type object[]
, that have been converted from the target value back to the source values. This method should return:
BindableProperty.UnsetValue
at positioni
to indicate that the converter is unable to provide a value for the source binding at indexi
, and that no value is to be set on it.Binding.DoNothing
at positioni
to indicate that no value is to be set on the source binding at indexi
.null
to indicate that the converter cannot perform the conversion or that it does not support conversion in this direction.
Consume a IMultiValueConverter
A IMultiValueConverter
is consumed by instantiating it in a resource dictionary, and then referencing it using the StaticResource
markup extension to set the MultiBinding.Converter
property:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:DataBindingDemos"
x:Class="DataBindingDemos.MultiBindingConverterPage"
Title="MultiBinding Converter demo">
<ContentPage.Resources>
<local:AllTrueMultiConverter x:Key="AllTrueConverter" />
<local:InverterConverter x:Key="InverterConverter" />
</ContentPage.Resources>
<CheckBox>
<CheckBox.IsChecked>
<MultiBinding Converter="{StaticResource AllTrueConverter}">
<Binding Path="Employee.IsOver16" />
<Binding Path="Employee.HasPassedTest" />
<Binding Path="Employee.IsSuspended"
Converter="{StaticResource InverterConverter}" />
</MultiBinding>
</CheckBox.IsChecked>
</CheckBox>
</ContentPage>
In this example, the MultiBinding
object uses the AllTrueMultiConverter
instance to set the CheckBox.IsChecked
property to true
, provided that the three Binding
objects evaluate to true
. Otherwise, the CheckBox.IsChecked
property is set to false
.
By default, the CheckBox.IsChecked
property uses a TwoWay
binding. Therefore, the ConvertBack
method of the AllTrueMultiConverter
instance is executed when the CheckBox
is unchecked by the user, which sets the source binding values to the value of the CheckBox.IsChecked
property.
The equivalent C# code is shown below:
public class MultiBindingConverterCodePage : ContentPage
{
public MultiBindingConverterCodePage()
{
BindingContext = new GroupViewModel();
CheckBox checkBox = new CheckBox();
checkBox.SetBinding(CheckBox.IsCheckedProperty, new MultiBinding
{
Bindings = new Collection<BindingBase>
{
new Binding("Employee1.IsOver16"),
new Binding("Employee1.HasPassedTest"),
new Binding("Employee1.IsSuspended", converter: new InverterConverter())
},
Converter = new AllTrueMultiConverter()
});
Title = "MultiBinding converter demo";
Content = checkBox;
}
}
Format strings
A MultiBinding
can format any multi-binding result that's displayed as a string, with the StringFormat
property. This property can be set to a standard .NET formatting string, with placeholders, that specifies how to format the multi-binding result:
<Label>
<Label.Text>
<MultiBinding StringFormat="{}{0} {1} {2}">
<Binding Path="Employee1.Forename" />
<Binding Path="Employee1.MiddleName" />
<Binding Path="Employee1.Surname" />
</MultiBinding>
</Label.Text>
</Label>
In this example, the StringFormat
property combines the three bound values into a single string that's displayed by the Label
.
The equivalent C# code is shown below:
Label label = new Label();
label.SetBinding(Label.TextProperty, new MultiBinding
{
Bindings = new Collection<BindingBase>
{
new Binding("Employee1.Forename"),
new Binding("Employee1.MiddleName"),
new Binding("Employee1.Surname")
},
StringFormat = "{0} {1} {2}"
});
Important
The number of parameters in a composite string format can't exceed the number of child Binding
objects in the MultiBinding
.
When setting the Converter
and StringFormat
properties, the converter is applied to the data value first, and then the StringFormat
is applied.
For more information about string formatting in Xamarin.Forms, see Xamarin.Forms String Formatting.
Provide fallback values
Data bindings can be made more robust by defining fallback values to use if the binding process fails. This can be accomplished by optionally defining the FallbackValue
and TargetNullValue
properties on a MultiBinding
object.
A MultiBinding
will use its FallbackValue
when the Convert
method of an IMultiValueConverter
instance returns BindableProperty.UnsetValue
, which indicates that the converter did not produce a value. A MultiBinding
will use its TargetNullValue
when the Convert
method of an IMultiValueConverter
instance returns null
, which indicates that the converter cannot perform the conversion.
For more information about binding fallbacks, see Xamarin.Forms Binding Fallbacks.
Nest MultiBinding objects
MultiBinding
objects can be nested so that multiple MultiBinding
objects are evaluated to return a value through an IMultiValueConverter
instance:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:DataBindingDemos"
x:Class="DataBindingDemos.NestedMultiBindingPage"
Title="Nested MultiBinding demo">
<ContentPage.Resources>
<local:AllTrueMultiConverter x:Key="AllTrueConverter" />
<local:AnyTrueMultiConverter x:Key="AnyTrueConverter" />
<local:InverterConverter x:Key="InverterConverter" />
</ContentPage.Resources>
<CheckBox>
<CheckBox.IsChecked>
<MultiBinding Converter="{StaticResource AnyTrueConverter}">
<MultiBinding Converter="{StaticResource AllTrueConverter}">
<Binding Path="Employee.IsOver16" />
<Binding Path="Employee.HasPassedTest" />
<Binding Path="Employee.IsSuspended" Converter="{StaticResource InverterConverter}" />
</MultiBinding>
<Binding Path="Employee.IsMonarch" />
</MultiBinding>
</CheckBox.IsChecked>
</CheckBox>
</ContentPage>
In this example, the MultiBinding
object uses its AnyTrueMultiConverter
instance to set the CheckBox.IsChecked
property to true
, provided that all of the Binding
objects in the inner MultiBinding
object evaluate to true
, or provided that the Binding
object in the outer MultiBinding
object evaluates to true
. Otherwise, the CheckBox.IsChecked
property is set to false
.
Use a RelativeSource binding in a MultiBinding
MultiBinding
objects support relative bindings, which provide the ability to set the binding source relative to the position of the binding target:
<ContentPage ...
xmlns:local="clr-namespace:DataBindingDemos"
xmlns:xct="clr-namespace:Xamarin.CommunityToolkit.UI.Views;assembly=Xamarin.CommunityToolkit">
<ContentPage.Resources>
<local:AllTrueMultiConverter x:Key="AllTrueConverter" />
<ControlTemplate x:Key="CardViewExpanderControlTemplate">
<xct:Expander BindingContext="{Binding Source={RelativeSource TemplatedParent}}"
IsExpanded="{Binding IsExpanded, Source={RelativeSource TemplatedParent}}"
BackgroundColor="{Binding CardColor}">
<xct:Expander.IsVisible>
<MultiBinding Converter="{StaticResource AllTrueConverter}">
<Binding Path="IsExpanded" />
<Binding Path="IsEnabled" />
</MultiBinding>
</xct:Expander.IsVisible>
<xct:Expander.Header>
<Grid>
<!-- XAML that defines Expander header goes here -->
</Grid>
</xct:Expander.Header>
<Grid>
<!-- XAML that defines Expander content goes here -->
</Grid>
</xct:Expander>
</ControlTemplate>
</ContentPage.Resources>
<StackLayout>
<controls:CardViewExpander BorderColor="DarkGray"
CardTitle="John Doe"
CardDescription="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla elit dolor, convallis non interdum."
IconBackgroundColor="SlateGray"
IconImageSource="user.png"
ControlTemplate="{StaticResource CardViewExpanderControlTemplate}"
IsEnabled="True"
IsExpanded="True" />
</StackLayout>
</ContentPage>
Note
The Expander
control is now part of the Xamarin Community Toolkit.
In this example, the TemplatedParent
relative binding mode is used to bind from within a control template to the runtime object instance to which the template is applied. The Expander
, which is the root element of the ControlTemplate
, has its BindingContext
set to the runtime object instance to which the template is applied. Therefore, the Expander
and its children resolve their binding expressions, and Binding
objects, against the properties of the CardViewExpander
object. The MultiBinding
uses the AllTrueMultiConverter
instance to set the Expander.IsVisible
property to true
provided that the two Binding
objects evaluate to true
. Otherwise, the Expander.IsVisible
property is set to false
.
For more information about relative bindings, see Xamarin.Forms Relative Bindings. For more information about control templates, see Xamarin.Forms Control Templates.