Home > WPF > MultiBinding in WPF

MultiBinding in WPF

January 21, 2009

In my current application, I have a ListBox that displays images horizontally.  Basically it is a WPF FilmStrip (without the control) with some additional features.  One feature is a toolbar that includes navigation buttons.  First and Last buttons are easy: they are always available and always navigate to the first or last image in the list.  The Previous and Next buttons are also easy to navigate, by simply incrementing or decrementing the SelectedIndex value of the ListBox.

Simple IValueConverter

While WPF seems to automatically handle numbers less than 0 and greater than the Items.Count, I only want the Previous and Next buttons enabled based on the SelectedIndex.  If the index is 0, I do not want the Previous button enabled, and if the index is the last one, I do not want the Next button enabled.  To accomplish this, I need to convert the SelectedIndex to a Boolean, so I wired up an IValueConverter.  In the case of Previous, it is a simple task in the converter to just compare the SelectedIndex to 0:

    public class SelectedItemIndexIsNotFirstToBoolean : IValueConverter
    {
        #region IValueConverter Members
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            if (value is int)
            {
                int index = (int)value;
                return (index > 0);
            }

            return false;
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }

        #endregion
    }

Basically, the converter returns True if the value is > 0 so that IsEnabled will be True.  Now to wire this up, just add the Converter to the Binding:


The XAML above does several things: first, the Path is the Property name of the Element (specified by ElementName) we want to send to the Converter. The Converter is a reference to a Resource instance of the Converter, which means you’ll have to add a line for this in Window.Resources:


If you aren’t impressed yet, think about what just happened: I bound a boolean value of a control (it could be any of them, not just IsEnabled) to the SelectedIndex value of a ListBox with no code behind or events. I don’t include the Converter classes as code behind, because I can easily see working up a DLL full of these and using them in many applications.

MultiBinding using IMultiValueConverter

The Next button, however, was not as straight forward as the Previous button.  In this case, I needed to check the SelectedIndex against the Items.Count property to see if this is the last Image in the list.  At first, I tried to use RelativeSource binding to navigate the tree and send the ListBox itself, but I quit for two reasons.  The first reason is it got hairy in a hurry I couldn’t get it to work.  The second reason is that I could see using the resulting IValueConverter for other applications and Control types, so I didn’t want to limit it.  The answer therefore became finding a way to send both the SelectedIndex and the Items.Count to a IMultiValueConverter.

A IMultiValueConverter functions much like an IValueConverter, receives an object[] as the first parameter.  Then, like the IValueConverter, we can simply process the array values and use them to determine the return value:

public class SelectedItemIndexIsNotLastToBoolean : IMultiValueConverter
{
    #region IMultiValueConverter Members

    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (values[0] is int && values[1] is int)
        {
            int index = (int)values[0];
            int count = (int)values[1];
            return (++index != count);
        }

        return false;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    #endregion
}

As you can see, other than processing multiple values, there is very little difference between the two.  The question then becomes “how the heck do I wire this thing up?”  If you are using Blend, I have bad news: as far as I can tell there is no way to wire up MultiBinding in Blend.  The good news is that it was really easy to do in XAML and required no RelativeSource or anything funky like that:


    
        
            
            
        
    
    

I broke the IsEnabled property out of the button and added a MultiBinding tag to it that contains all the magic.  You have to specify the Binding tags in the order the IMultiValueConverter is expecting them.  The statement is very simple, referencing the ElementName and Path as above for each value to be passed to the Converter.  I did discover something interesting: look at the second one and you’ll see I indicated “Items.Count”.  I didn’t actually think this would work since I am referencing a subproperty, but the Count came through just fine. And of course, don’t forget to add a reference to the Converter to Window.Resources.

Conclusions

There is some concern out there that too much stuff will get shoved into XAML just because we can.  The question you have to ask yourself is whether or not the time invested and the result are worth it.  In this case, I think it is justified.  Most of the time I invested here was learning the technique, which is a cost I won’t have to pay again.  It also ended in much less coding in my button Click events, and is completely reusable for different scenarios and future projects.

Advertisement
Categories: WPF
  1. September 20, 2010 at 11:56 am

    <prog:SelectedItemIndexIsNotFirstToBoolean x:Key="Selecte

    What for does "prog" stand?

  2. September 22, 2010 at 11:19 am

    Hi serhio,

    In this case, “prog” represents the Namespace where the SelectedItemIndexIsNotFirstToBoolean class is defined.

    The reference (like using in C# or Imports in VB) is defined in XAML like so:

    xmlns:prog=”clr-namespace:MyNamespace”

    “prog” can be whatever identifier you want for that namespace.

  3. June 13, 2011 at 10:28 am

    Perfect. Thanks.

  1. No trackbacks yet.
Comments are closed.
%d bloggers like this: