Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Silverlight Multi-Binding

0.00/5 (No votes)
17 Nov 2011 3  
Performing multi-binding in Silverlight.

Screen.GIF

Introduction

Multi-binding is a very important/powerful feature in WPF. It comes in handy when you want to get a single result that depends on multiple inputs. The result is bound to a special logic often implemented inside a 'MultiValueConverter'. So any change to any of the inputs should be manifested in the output result. In Silverlight, this feature is missing!

In this article, I'll show three solutions I came up with, from 'Quick & Dirty' to the 'WPF-look-alike'.

Background

While working on porting a WPF app to Silverlight, I encountered this 'missing feature' issue (which Silverlight programmers are all familiar with...) as Silverlight is only a subset of WPF. Like any other software developer, the first thing I did was to Google it...

After examining the suggested approaches, I came out with these conclusions:

  1. Some of the solutions where too smart/complex for my 'taste'.
  2. The solutions weren't as robust as I hoped.
  3. For example:

    • Lack of support for DependencyObject multi-binding (only FrameworkElement)
    • Lack of RelativeSource in Binding.
  4. The implementation was too far away from the base-line WPF way (in some cases awkward) which makes porting to Silverlight too messy.

So, I thought I'll give it a try and solve it my own way...

Some 'hints' led me to the solutions I came up with:

  1. MS kept excluding this feature throughout all Silverlight versions, which could have been due to two reasons:
    1. MS is indifferent to its developers' needs...
    2. MS 'thinks' that this feature is relatively simple to achieve using the standard available tools.
    3. I found it was more fruitful for me to take the approach that for any algorithmic problem I encounter, there is a simple, elegant, and quick solution, I just need to find it... so I considered the second reason as the 'real' one.

  2. Thinking of the core functionality of multibinding should 'ring' one's bell:
  3. Which standard Silverlight feature has the 'ability' to monitor and respond to property changes? Yes, DependencyProperty! and it's variant AttachedProperty.

  4. New features introduced in Silverlight 5.0:
    • RelativeSource with AncestorType
    • MarkupExtension (in short -'auto' instantiated object (in XAML) that has a return value).

Having these 'hints' in mind, I came out with three solutions:

1. Quick & Dirty

If you have only the multibinding functionality in mind, you can get along with this approach:

  • Construct a 'specialized' class with AttachedProperties matching your input bindings (you can even give these properties meaningful names).
  • Relay any changes of any of these AttachedProperties to a single 'Any...Changed' subroutine on which you will do two things:
    1. Perform your desired multibinding logic.
    2. Update the 'outcome property'.

2. Partial-Elegant

As best practice it is better to seperate multibinding to seperate tasks:

  1. Multi-input-bindings generic 'engine' (for XAML)
  2. Multi-values-into-one logic (usually as an implementation of IMultiValueConverter).

Having that in mind, we can create an 'AttachedProperty' based class (called MultiBindingUtil) that will have any number (for simplicity's sake, the sample has only three pre-defined ones) of Attached Properties for the input bindings (called Binding1 to Binding(n)), AttachedProperty for the Converter, and finally, an AttachedProperty for the result (called MBResult in the sample). Now all we have to do in XAML is to bind the input binding AttachedProperties to whatever we like, supply a converter, and bind the MBResult back to the property we would like to be changed by the multibinding. Inside the multibinding 'engine', just like the previous example, we'll relay any input-binding changes into a single 'Any...Changed' subroutine; only, there, we'll update the MBResult property with the converted result of the supplied converter, using the class's bindings as the input. All is well, except there is a well documented/reported design-time bug occurring in Silverlight when binding an element's property to its own attached property. This bug manifests itself in a very subtle and informative way:

Ms_AttachedPropRef_Bug.JPG

I couldn't find any solution for this 'local-path' issue. If any of you know how to fix it, please share.

3. WPF-Alike

This solution gives a very close XAML look as WPF's multibinding, which in cases of porting might be helpful. Also, it utilizes the new Silverlight 5.0 MarkupExtension support and the previous solution's MultiBindingUtil class. Here we are using a new class of type MarkupExtension called 'MyMultiBinding(Extension)', which has a Content property List<Binding> called (oddly) 'Bindings' and a Converter property of type IMultiValueConverter called (oddly) 'Converter'.

Setting this property as content is done using this attribute:

[ContentProperty("Bindings")]

The actual trick is done inside the MarkupExtension's most significant override method:

override object ProvideValue(IServiceProvider serviceProvider)

We'll do the following actions:

  1. Get the 'target' FrameworkElement:
  2. IProvideValueTarget pvt = 
      serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
    DependencyObject  TargetObject = pvt.TargetObject as DependencyObject ;
  3. 'Attach' to it MultiBindingUtil's bindings properties with matching values of 'our' Bindings list.
  4. for (int i = 0; i < _Bindings.Count ; i++)
    {
        string sDpName="Binding"+(i+1).ToString()+"Property";
        //using reflection to get the DependencyProperty from type/string
        FieldInfo fi = typeof(MultiBindingUtil).GetField(sDpName);
        if (fi==null)
        {
            throw new IndexOutOfRangeException(
              "MultiBindingUtil Max number of binding(3) exceeded");
        }
        DependencyProperty dp = (DependencyProperty)fi.GetValue(null);
        BindingExpressionBase beb = 
          BindingOperations.SetBinding(TargetObject, dp, _Bindings[i]);                   
    }
  5. 'Attach' to it MultiBindingUtil's Converter property with value of 'our' Converter.
  6. TargetObject.SetValue(MultiBindingUtil.ConverterProperty, this.Converter);
  7. Set the return value to Binding (rather than a simple value). This binding will bind to MBResult MultiBindingUtil's attached property(!).
  8. Binding b = new Binding();
    b.Source = TargetObject;
    b.Path =new PropertyPath(MultiBindingUtil.MBResultProperty);
    return b;

Remarks

  1. Solutions 1, 2 should be compatible with Silverlight 4.0.
  2. Solution 3 doesn't work for Style Setter multi-binding.
  3. The Quick & Dirty solution (#1) can help there, like that:

    <Setter Property="mb:MultiBindingReplacement.EpisodeLength" 
            Value="{Binding Path=RangeMilSec}" />
    <Setter Property="mb:MultiBindingReplacement.Scale" 
            Value="{Binding Path=VM.ScaleTransScaleX}" />

    Where MultiBindingReplacement is the name of the special class, and EpisodeLength and Scale are special DependencyPropertys for the bindings.

    Inside the class, I perform the 'conversion logic' and set the result to a special property in the Style's targeted object (as explained in solution 3).

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here