Skip Navigation LinksHome > View Post

MultiBinding for Silverlight 3

One of the key binding features missing from Silverlight 3 is MultiBinding and IMultiValueConverter support. In this post I'll walk through a custom implementation that uses a few tricks that you may want to leverage elsewhere.

First of all, I wanted to re-create the IMultiValueConverter from WPF in it's entirety. Here it is:

public interface IMultiValueConverter
{
    object Convert(object[] values, Type targetType, object parameter, CultureInfo culture);
    object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture);
}

That was easy. Now for the actual MultiBinding. In WPF a MultiBinding looks like this:

<TextBlock Name="textBox2" DataContext="{StaticResource NameListData}">
    <TextBlock.Text>
        <MultiBinding
            Converter="{StaticResource myNameConverter}"
            ConverterParameter="FormatLastFirst">
                <Binding Path="FirstName"/>
                <Binding Path="LastName"/>
        </MultiBinding>
    </TextBlock.Text>
</TextBlock>

Ideally I'd have liked to recreate this for Silverlight but without support for custom MarkupExtensions that's just not happening. Instead, we'll have to use another trick where we implement a non-visual FrameworkElement. We use a FrameworkElement because we want DependencyProperties that support Bindings and these have to belong to FrameworkElement in Silverlight (a DependencyObject isn't good enough in SL3 as it is in WPF). No matter.

The other trick is that we have to add this element to the Visual Tree. What? Yes, I know, it feels like a hack but really it isn't. Even the RIA team do it so it must be OK. The key thing is that our element is non-visual so it doesn't contribute to the Render pipeline (has 0x0 size etc...). This is what it might look like in use:

<binding:MultiBinding x:Name="mb" Converter="{StaticResource intsToBrushConverter}"
    NumberOfInputs="3"
    Input1="{Binding ElementName=red, Path=Value, Mode=TwoWay}"
    Input2="{Binding ElementName=green, Path=Value, Mode=TwoWay}"
    Input3="{Binding ElementName=blue, Path=Value, Mode=TwoWay}" />

<Border Background="{Binding ElementName=mb, Path=Output}" Margin="5"/>

<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Grid.Row="1">
    <Slider x:Name="red" Minimum="0" Maximum="255" Margin="5" Orientation="Vertical"/>
    <Slider x:Name="green" Minimum="0" Maximum="255" Margin="5" Orientation="Vertical" />
    <Slider x:Name="blue" Minimum="0" Maximum="255" Margin="5" Orientation="Vertical" />
</StackPanel>

You might have guessed that I'm creating a Color Selector control. Wanna' see it in action? Go on then (can't see it? click here).

Here's the BrushConverter (IMultiConverter) that's used by the MultiBinding:

public class IntsToBrushConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        byte r = System.Convert.ToByte(values[0]);
        byte g = System.Convert.ToByte(values[1]);
        byte b = System.Convert.ToByte(values[2]);
        return new SolidColorBrush(Color.FromArgb(Byte.MaxValue, r, g, b));
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture
    {
        SolidColorBrush brush = (SolidColorBrush)value;
        return new object[] { brush.Color.R, brush.Color.G, brush.Color.B };
    }
}

Easy peasy. Want the code? Go on then:

  • Usual disclaimers apply - this is demoware and used very much at your own risk.
  • Download Source (12 KB)
  • Please leave some feedback in the comments if you like it.

Important Notes

You may have noticed a few strange features about our MultiBinding, e.g. how the inputs are specified on a number of Input1, Input2 and Input3 properties instead of on child controls. That's just the way it is I'm afraid (because Bindings only work on members of the visual tree, and since this is a non-visual element any child elements aren't part of the visual tree), and because of this you have to specify the NumberOfInputs you want - my version supports up to 5.

<binding:MultiBinding x:Name="mb" Converter="{StaticResource intsToBrushConverter}"
    NumberOfInputs="3"
    Input1="{Binding ElementName=red, Path=Value, Mode=TwoWay}"
    Input2="{Binding ElementName=green, Path=Value, Mode=TwoWay}"
    Input3="{Binding ElementName=blue, Path=Value, Mode=TwoWay}" />

You really can drop this control pretty much anywhere in your visual tree but it makes most sense to keep it close to the parts that need it. As always, your own mileage may vary. Any feedback appreciated in the comments.

 
Josh Post By Josh Twist
10:27 AM
23 Sep 2009

» Next Post: Five minute recipe for a decent BoolToVisibilityConverter
« Previous Post: .NET Naming Conventions

Comments are closed for this post.

Posted by Terry Carvin @ 09 Oct 2009 1:02 PM
Hi Josh, great work as ever, just a note to say that when I tried the link "can't see it? click here" it came up with "page not found". Link location: http://www.thejoyofcode.com/uploads/MultiBinding_for_Silverlight_3.aspx

Sorted my Silverlight issue out now but was using new install of Firefox 3.5.3. Hehe sacrilege I know. If only IE had add-block plus lol

Terry.

Posted by Josh @ 10 Oct 2009 12:16 AM
Well I never, is that _the_ Terry Carvin? :D

Cheers, fixed the link - it just points back to this page for those viewing in a feedreader etc.

Ta

Posted by RredCat @ 03 Nov 2009 3:18 AM
What does class Bindorama do??

Posted by josh @ 03 Nov 2009 4:21 AM
@Rredcat - check this:http://www.thejoyofcode.com/Bindorama_Binding_Craziness_now_for_Silverlight_too.aspx

Posted by archana @ 17 Nov 2009 7:06 AM
nice blog.Only doubt i have y we are using dispose here?

Posted by Justin @ 18 Mar 2010 11:57 AM
Hey Josh. Great post was exactly what I was looking for.

I did have a small problem with the MultiBinding not getting kicked off. I noticed that you have a "_ready" flag in the UpdateOutput(). I was binding values from the "DataContext" instead of UIElements as in your example. I found that the Output was not intialized properly because when DataContext was set "_ready" was false.

Very simple fix which I suggest you add to your code. When you set _ready = true you should call UpdateOutput.

public MultiBinding()
{
this.Loaded += delegate
{
_ready = true;
this.UpdateOutput(); };
}
}

Posted by mjsp @ 08 Jun 2011 1:02 PM
Thanks this is AWESOME.

Had exactly same problem as Justin where I was binding to content that was set from the DataContext 'early on', and yeah that fix is neater than the one I originally applied ;)

© 2005 - 2014 Josh Twist - All Rights Reserved.