MultiBinding in Xamarin.Forms

,

The current release of Xamarin.Forms does not contain an implementation for a MultiBinding object. For those of us who have a strong WPF background, this is a feature that would be very beneficial. With a little work, it is possible for us to implement our own MultiBinding class using the current Xamarin.Forms framework.

Creating a basic MultiBinding

The existing Binding class  is the glue that links any property on the binding’s source to a BindableProperty on the target BindableObject. Typically, this will be a property on a VisualElement. A simple binding might look like this:

This is great when we only want to bind a single value, but what about when we have multiple values we want to use. This is the problem that a MultiBinding is designed to solve when combined with a string format or a converter.
This is where we will start with our own MultiBinding class.

Since we can’t derive from the Xamarin.Forms Binding (sealed class) nor BindingBase (constructor is declared internal) classes, we will declare our MultiBinding class as a IMarkupExtension<Binding>. It is important to use IMarkupExtenion<T> rather than IMarkupExtension if you are going to use the XAML Compile feature.

In the ProvideValue method we will create the bindings and links that cause the MultiBinding to work. We will then monitor the dynamic BindableProperties we create for each binding in the Bindings collection and update our own internal value when any of them change. We will create a Binding using this internal value as its source and return it as the MultiBinding.

Looking a bit deeper at the ProvideValue method, we first get access to the object that the MultiBinding is getting applied to by using the IProvideValueTarget service interface. We use this object to make calls to SetBinding and GetValue. The advantage of using this object is that our bindings will get the same BindingContext as the object that the MultiBinding is applied to.

For each of our bindings we create a bindable property as their target. For our internal value there is a private inner class that implements INPC. This property could have been left on the MultiBinding itself, but since it needs to be public, I find that hiding it away in an internal class keeps the API of the MultiBinding cleaner.

Adding IMultiValueConverter

If we run the app, we can see the MultiBinding works with string formats, but this is only a fraction of the functionality that the WPF MultiBinding provides. In WPF we can specify an IMultiValueConverter to synthesize the bindings’ values into a single value. Let’s add similar functionality to our own MultiBinding class.

First we declare the converter interface. Most of the time MultiBindings are only used to convert from the source to the target, so we will only concern ourselves with one-way conversions.

Next, we will add properties for the converter and its parameter to the MultiBinding, and update the relevant portions of the ProvideValue method.

In an effort to mimic WPF behavior, the converter is applied prior to the string format. We need to modify our SetValue method. Rather than applying the string format there, we will do it in our new MultiValueConverterWrapper.

In this new structure we will always pass an array of values from our bound properties to the internal value. We then apply the string format and converter inside of the MultiValueConverterWrapper.

This allows us to create our own multi value converters that can translate an array of objects into our desired type. In the following example, our value converter simply selects the first non-null value from the array or the converter’s parameter if there are no non-null values. It then takes the value and applies a string format to it.

Handling triggers, styles, and setters

It is worth pointing out that our MultiBinding, in its current state, will fail if it is used inside of a setter (applied either directly on a VisualElement’s trigger or as part of a Style). This is because in we are using the TargetObject from the IProvideValueTarget service. Because a setter is not a BindableObject the cast fails and we have no BindableObject to use to set the bindings on. If all you care about is having a MultiBinding that you can apply directly to elements, you can stop here.

The following options are very hacky, kittens will be killed, and you may be devoured by a raptor.

With that warning out of the way, let’s continue.

Handling the case where the MultiBinding is used directly inside of a VisualElement’s trigger can be solved by accessing some internal members. The two classes that currently exist in Xamarin.Forms that implement the IProvideValueTarget interface also implement another internal interface IProvideParentValues interface.

Using reflection, we could retrieve the parent objects from our IProvideValueTarget. By doing this we can simply grab the first BindableObject that we find in the ParentObjects. This will effectively handle the case where the MultiBinding is used within a Setter that is a child of the target element:

This does not solve the case where the setter is not a child of the element (such is the case when used inside of a style):


This case is much more complicated. Because multiple elements can share a single style, when a setter’s value derives from BindingBase it creates a shallow clone of the binding and applies the cloned binding to the target element. This is a problem for our current implementation because it would apply a shallow clone of the binding returned from the ProvideValue method. However, there is a solution.Examining the manifest of the Xamarin.Forms.Core assembly we can see that contains several InternalsVisibleToAttributes. Specifically we are going to pick on this one: We can create our own proxy PCL project that has an Assembly name of “Xamarin.Forms.Core.UnitTests”. This project can be referenced by our main PCL project, and will have access to all of the internal members of the Xamarin.Forms.Core assembly.Screen Shot 2016-03-15 at 11.40.37 AMThis now allows us to derive from BindingBase and implement a true MultiBindings class.

Rather than using a markup extension we can simply derive from BindingBase directly. Because we are deriving from BindingBase we no longer need our own StringFormat property since it is declared on the base class. The Converter and ConverterParameter properties are now implemented with a backing field so that we can throw if they get modified after the binding is applied.

We also have access to the internal BindingExpression class. We will use it in a similar manner as Xamarin’s Binding class to get similar behavior.

The apply and unapply methods are invoked as the MultiBinding is applied to an object, or when its binding context changes. Because we now have knowledge of when we are being applied to an element, we can properly create our child properties and bindings. The child bindings use the actual context object, while the MultiBinding itself uses the InternalValue class as its source. Our previous MultiValueConverterWrapper is replaced by similar logic inside of the GetSourceValue method that is used to provide a value from the MultiBinding to the target property.

Finally, the clone method solves the problem of the MultiBinding being used inside of a style’s setter. Because bindings are cloned when a style is applied to an element, deriving from BindingBase and implementing the Clone method allows the MultiBinding to work correctly.ConclusionThough Xamarin.Forms is certainly maturing, it still lacks some of the functionality that WPF developers would expect. It would be ideal if the library exposed more functionality and opportunities for developers to extend and implement these types of features. Hopefully this will improve in the future.Full code can be found on Github.The code for the simple binding (the one that won’t work inside of setters) can be found here.
Share this story

11 responses to “MultiBinding in Xamarin.Forms

  1. Hi!
    This is pure magic, thank you very much for your effort.
    This works great, I missed multi bindings so much in Xamarin and finally can use them! However I experienced a problem when using Converters for the binded values and want to propose a solution to it (I’m using the approach with the fake assembly name “Xamarin.Forms.UnitTests” from your repo):

    What causes the problem:
    ~~~~~~~~~~~~~~~~~~~~~

    What is the problem:
    ~~~~~~~~~~~~~~~~~
    If the converter is used, then the bindableObj.GetValues() in SetInternalValue will always contain old values, meaning that a change caused by the converter is not considered (SetInternalValue is invoked, but GetValues will deliver the old value). Then, the AllTrueConverter in the example above will receive wrong values (I didn’t really get why, the converter ANormalConverter is invoked and returns the correct value, but AllTrueConverter receives the old value then)

    How I fixed the problem for me:
    ~~~~~~~~~~~~~~~~~~~~~~~~~~
    1) Create an extra class for providing the BindingPropertyChangedDelegate in MultiBinding.cs, which will allow us to replace the value at the matching index:
    private sealed class PropertyChangedMappingProvider
    {
    public PropertyChangedMappingProvider(int index, MultiBinding reference)
    {
    this.ChangedDelegate = (bindableObj, o, n) =>
    {
    reference.SetInternalValue(bindableObj, index, o, n);
    };
    }

    public BindableProperty.BindingPropertyChangedDelegate ChangedDelegate;
    }

    2) Change the assignment of the BindingPropertyChangedDelegate in Method “Apply”:
    PropertyChangedMappingProvider changedProvider = new PropertyChangedMappingProvider(i, this);
    var property = BindableProperty.Create($”{nameof(MultiBinding)}Property-{Guid.NewGuid():N}”, typeof(object),
    typeof(MultiBinding), default(object), propertyChanged: changedProvider.ChangedDelegate);

    3) Change SetInternalValue:
    private void SetInternalValue(BindableObject source, int index = -1, object oldValue = null, object newValue = null)
    {
    if (source == null || isApplying) return;

    if (index >=0 && index < Properties.Length)
    {
    var values = source.GetValues(Properties);
    // Be sure to override the changed property with the new value
    values[index] = newValue;
    internalValue.Value = values;
    }
    else
    {
    internalValue.Value = source.GetValues(Properties);
    }
    }

    I struggled a lot to find the cause and I guess this is a very, very dirty fix, though it was interesting to debug through it and see how all glues together.
    Maybe you might consider to provide a more elegant solution than this in the future ;)

    Warm regards,
    Mathias Lackner

  2. After updating to Xamarin.Forms 2.5.1.444934 I can no longer build this project. I’m assuming that the InternalsVisibleTo attribute has been removed for the unit testing assembly.

    1. Although in browsing the source online, it appears to still be there. If that be the case, I’m not quite sure why upgrading to the latest Xamarin.Forms would cause the project to fail compilation.

    2. So I just now reverted back to Xamarin.Forms 2.5.0.280555 and the project compiles just fine once again. I’m perplexed.

    3. Well, I finally realized that an additional optional parameter was added to the Apply and Unapply methods.
      It works now after adding those!

      1. Hi Gregory,

        Sorry for the confusion. It sounds like you figured it out. I have updated the code in github and in this post to reflect the latest API in Xamarin.Forms 2.5.1

  3. Hi Kevin, Love the work on this one! But I can’t figure out where FirstNotNullConverter is defined? I am getting

    Additional information: Position 22:8. Type helper:FirstNotNullConverter not found in xmlns clr-namespace:Ximon.Helpers;assembly=Ximon

    Yes, I checked my namespace and it is OK.

    I copied the code directly from GitHub (I used the NON-SETTER version)

    And My XAML:

    ….

    Any ideas? Thanks!!!

    1. Hi Jim,

      The converter is a very simple class that simply grabs the first value that is not null. Here is the complete code for it:

      using System;
      using System.Globalization;
      using System.Linq;
      using Xamarin.Forms.Proxy;

      namespace MultiBindingExample
      {
      public class FirstNotNullConverter : IMultiValueConverter
      {
      public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
      {
      return values.FirstOrDefault(x => x != null) ?? parameter;
      }
      }
      }

Leave a Reply

Your email address will not be published. Required fields are marked *