Louis Kottmann
Louis Kottmann

Reputation: 16648

Nested MultiBinding(s)

I've been playing around with WPF for quite a while now, but for the first time today, I needed to nest a MultiBinding inside another, something like:

<MultiBinding>
   <Binding />       
   <MultiBinding>
      <Binding />
      <Binding />
   </MultiBinding>
</MultiBinding>

I get an exception indicating it's not allowed by the framework:

XamlParseException was unhandled: Add value to collection of type 'System.Collections.ObjectModel.Collection(System.Windows.Data.BindingBase)' threw an exception.

The InnerException is more explicit:

BindingCollection does not support items of type MultiBinding. Only Binding is allowed.

So digging the web for more info, I stumbled upon this Microsoft Connect issue which is exactly my problem.

Thank you for the feedback. WPF doesn't support this today. This feature has been requested for years (most recently earlier this month - see https://connect.microsoft.com/WPF/feedback/details/650164/nested-multibinding). We'll continue to consider this for future releases.

Right now I've made my peace that I won't have it easy. Still I need this, how can I nest MultiBindings?

Upvotes: 25

Views: 11043

Answers (7)

Alexey Zakharov
Alexey Zakharov

Reputation: 1

You can use a simple binding proxy as well.

<DataGrid.ContextMenu>
    <ContextMenu>
        ...
        <MenuItem Click="MenuItem_Click" Header="Use in filter" Command="{Binding SetCatFilterCommand}">
            <MenuItem.Resources>
                <local:BindingProxy x:Key="AsLitterIsEnabled" >
                    <local:BindingProxy.Data>
                        <MultiBinding Converter="{local:ViewCatConverter}"  
                            ConverterParameter="AsLitterIsEnabled">
                            <Binding Path="Heart.Cat" Mode="OneWay"/>
                            <Binding Path="Heart.SelectedLitters"                                 Mode="OneWay"/>
                        </MultiBinding>
                    </local:BindingProxy.Data>
                </local:BindingProxy>
            </MenuItem.Resources>
            <MenuItem.CommandParameter>
                <MultiBinding Converter="{local:MultiValueToArrayConverter}">
                    <Binding Path="Heart.SelectedLitters"/>
                    <Binding Source="AsLitter"/>
                    <Binding Path="CatFilter"/>
                    <Binding Path="Data" Source="{StaticResource AsLitterIsEnabled}"/>
                </MultiBinding>
             </MenuItem.CommandParameter>
         </MenuItem>
         ...
    </ContextMenu>
</DataGrid.ContextMenu>

The BindingProxy class:

using System.Windows;

public class BindingProxy : Freezable
{
    public static readonly DependencyProperty DataProperty =
        DependencyProperty.Register("Data", typeof(object),
        typeof(BindingProxy));

    public object Data
    {
        get => GetValue(DataProperty);
        set => SetValue(DataProperty, value);
    }

    protected override Freezable CreateInstanceCore()
    {
        return new BindingProxy();
    }
}

Upvotes: 0

GoLDie
GoLDie

Reputation: 119

I know that this is an old question but I think this this is a much nicer approach:

<FrameworkElement x:Name="IsBuyAndAmountInReference">
    <FrameworkElement.Tag>
        <MultiBinding Converter="{StaticResource LogicAndToBool}">
            <Binding Path="OrderData.IsBuy" />
            <Binding Path="OrderData.AmountInReference" />
        </MultiBinding>
    </FrameworkElement.Tag>
</FrameworkElement>
<FrameworkElement x:Name="IsSellAndAmountInBase">
    <FrameworkElement.Tag>
        <MultiBinding Converter="{StaticResource LogicAndToBool}">
            <Binding Path="OrderData.IsBuy" Converter="{StaticResource BooleanToBooleanInvert}" />
            <Binding Path="OrderData.AmountInReference" Converter="{StaticResource BooleanToBooleanInvert}" />
        </MultiBinding>
    </FrameworkElement.Tag>
</FrameworkElement>

<Slider Grid.Row="2" Grid.ColumnSpan="4">
    <Slider.Visibility>
        <MultiBinding Converter="{StaticResource LogicOrToVisibility}">
            <Binding ElementName="IsBuyAndAmountInReference" Path="Tag" />
            <Binding ElementName="IsSellAndAmountInBase" Path="Tag" />
        </MultiBinding>
    </Slider.Visibility>
</Slider>

Upvotes: 11

Matt Thomas
Matt Thomas

Reputation: 5794

An alternative to the other suggestions is to use attached properties to hold nested MultiBindings as intermediate values.

For example, instead of:

<Element>
  <Element.Property>
    <MultiBinding>
      <Binding Path="A" />       
      <MultiBinding>
        <Binding Path="B" />
        <Binding Path="C" />
      </MultiBinding>
    </MultiBinding>
  </Element.Property>
</Element>

...do this:

<Element Name="ElementName">
  <ElementProperties.AttachedProperty>
    <MultiBinding>
      <Binding Path="B" />
      <Binding Path="C" />
    </MultiBinding>
  </ElementProperties.AttachedProperty>
  <Element.Property>
    <MultiBinding>
      <Binding Path="A" />       
      <Binding ElementName="ElementName" Path="(ElementProperties.AttachedProperty)" />
    </MultiBinding>
  </Element.Property>
</Element>

I know this question is more than six years old now, but I ran into it so someone else will, too.

Upvotes: 7

Wolfshead
Wolfshead

Reputation: 550

I realise that this is an old question now, but I just hit exactly the same problem as the OP. Fortunately in my case I could bind to a sub-element where the result of the multi-binding was already being calculated, but it got me thinking...

A (though admittedly not very clean) solution would be to write the value of the multi-value binding into a 'spare' property such as an element's 'Tag' which you can then reference in your other multi-value bindings by specifying the 'ElementName' attribute.

If you need more than a single nested multi-value binding then you could create a 'fake' object with some dependency properties on it to store multiple intermediate results.

A pity that Microsoft don't implement a properly nested system...

Upvotes: 5

blindmeis
blindmeis

Reputation: 22445

simply use one multibinding an a MultiConverter.

or what i prefer expose your condition in one property of your viewmodel/datacontext.

Upvotes: 0

Dan Puzey
Dan Puzey

Reputation: 34218

If you have a converter that takes a parameter, you can do something like this:

  • Create a class for passing the "fixed" data to your converter
  • Add DependencyProperties to the class (so that you can bind the values in Xaml)
  • In your xaml, use a binding with a converter instead of a multibinding, something like this:

    <MultiBinding>
        <Binding Source="SomeObject" Path="CoreValue" Converter="{StaticResource YourNewConverter}">
            <Binding.ConverterParameter>
                <ns:ParameterClass Value1="{Binding Parameter1}" Value2="{Binding Parameter1}" />
            </Binding.ConverterParameter>
        </Binding>
     .... 
    

The limitation is that (AFAIK) the value will only be recalculated if CoreValue changes - it won't automatically rebind if the converter parameters change.

(Apologies for any errors, I'm typing this without the benefit of VS to test in...)

Upvotes: 5

Luis Filipe
Luis Filipe

Reputation: 8718

If you're binding to a string you can use StringFormat just like in this example:

 <TextBlock>
   <TextBlock.Text>
     <MultiBinding StringFormat="{}{0:0.###}/{1:0.###}" Mode="OneWay">
                        <Binding ElementName="This" Path="AggregatedDocDetail.ConfirmedQty"></Binding>
                        <Binding ElementName="This" Path="AggregatedDocDetail.Qty">   </Binding>
       </MultiBinding>
     </TextBlock.Text>
   </TextBlock>

Upvotes: 0

Related Questions