多层次MultiBinding

25

我已经玩弄WPF相当长一段时间了,但今天第一次需要嵌套一个MultiBinding到另一个中,例如:

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

我收到了一个异常,指示框架不允许这样做:

  

XamlParseException未处理:向类型为'System.Collections.ObjectModel.Collection(System.Windows.Data.BindingBase)'的集合添加值引发了异常。

InnerException更明确:

  

BindingCollection不支持MultiBinding类型的项。仅允许使用Binding。

于是我在网上搜索了更多信息,我偶然发现了这个Microsoft Connect问题,这正是我的问题。

  

感谢您的反馈。WPF今天不支持此功能。多年来一直有人请求此功能(最近是本月早些时候 - 请参见https://connect.microsoft.com/WPF/feedback/details/650164/nested-multibinding)。我们将继续考虑这个功能以用于未来的版本。

现在我已经接受了事实,我想知道如何嵌套MultiBindings


你想做什么,需要嵌套多绑定? - Dan Puzey
我正在转换具有复杂优先级顺序的布尔值,虽然其中一些已经是布尔值,但其他需要首先进行转换,因此它们本身嵌入在其他IMultiValueConverter中。 - Louis Kottmann
如果您的其他MultiValueConverters没有超过两个输入值,您可以使用带有参数的转换器...或者您可以使用带有复杂参数的转换器来输入多个值,这需要一些工作,但应该可以解决问题。如果听起来可行,我可以发布一个示例吗? - Dan Puzey
就参数而言,我们假设大多数转换器只接受两个输入。该参数无法绑定,因为它不是一个依赖属性 :/ - Louis Kottmann
当然,我可以利用Hillberg的可冻结技巧,或者DatacontextSpy等工具,但如果您曾经使用过它,您就知道它很不稳定... - Louis Kottmann
有绑定非依赖属性的方法,可以使用一些肮脏的附加属性 - 不像 Hillberg 的那么松散,只要您能在限制范围内工作即可。之前没有接触过 DataContextSpy... - Dan Puzey
7个回答

8

我知道这是一个老问题,但我认为这是一种更好的方法:

<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>

7

除了其他建议外,另一种方法是使用附加属性来保存嵌套的MultiBinding作为中间值。

例如,可以使用以下方式:

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

要做到这一点:

<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>

我知道这个问题已经六年以上了,但是我也遇到了这个问题,所以其他人可能也会遇到。


5
我知道这是一个旧问题了,但我刚好遇到了与OP完全相同的问题。幸运的是,在我的情况下,我可以绑定到已经计算出多重绑定结果的子元素,但这让我想到了一些事情...
一个(虽然不太干净)的解决方法是将多值绑定的值写入一个“备用”属性(如元素的“Tag”)中,然后通过指定“ElementName”属性在其他多值绑定中引用它。
如果你需要超过一个嵌套的多值绑定,那么你可以创建一个“假”的对象,并在其上创建一些依赖属性来存储多个中间结果。
遗憾的是,Microsoft没有实现一个真正嵌套的系统...

5
如果您有一个带参数的转换器,您可以像这样操作:
  • 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>
     .... 
    
限制在于(据我所知),只有当 CoreValue 更改时,该值才会重新计算 - 如果转换器参数更改,则不会自动重新绑定。
(如果有错误,抱歉,我没有VS测试无法确定...)

我认为这是一个有效的想法,但要优化继承DataContext以便可以将ParameterClass的属性绑定到它上会花费太长时间。 - Louis Kottmann

0

只需使用一个Multibinding和MultiConverter即可。

或者,我更喜欢在您的ViewModel/DataContext中公开您的条件属性。


12
是的,但这样做违背了整个目的。如果我需要创建一个新的转换器,只是将其他转换器组合起来,而我有大约一百个转换器......那就有很多重复工作。 - Louis Kottmann
确实如此,但我正在尝试重复使用我已经拥有的东西 ;) - Louis Kottmann
1
你能否在ViewModel的一个属性中公开你的条件?这样你就不需要使用MultiConverter/MultiBinding了。 - blindmeis
我最终做了类似的事情。 - Louis Kottmann

0
您也可以使用一个简单的绑定代理。
<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>

BindingProxy 类:

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();
    }
}

0
如果您要绑定到字符串,可以像这个例子一样使用StringFormat:
 <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>

我正在绑定到布尔值(请参见问题的注释),还绑定到POCOs。 - Louis Kottmann

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接