2010-08-04 106 views
84

我有一个ListBox绑定到ViewModel上的子集合。从DataTemplate访问父DataContext

<Style x:Key="curveSpeedNonConstantParameterCell"> 
    <Style.Triggers> 
     <DataTrigger Binding="{Binding Path=DataContext.CurveSpeedMustBeSpecified, 
      ElementName=someParentElementWithReferenceToRootDataContext}" 
      Value="True"> 
      <Setter Property="Control.Visibility" Value="Hidden"></Setter> 
     </DataTrigger> 
    </Style.Triggers> 
</Style> 

我得到以下输出错误:

System.Windows.Data Error: 39 : BindingExpression path error: 
'CurveSpeedMustBeSpecified' property not found on 
    'object' ''BindingListCollectionView' (HashCode=20467555)'. 
BindingExpression:Path=DataContext.CurveSpeedMustBeSpecified; 
DataItem='Grid' (Name='nonConstantCurveParametersGrid'); 
target element is 'TextBox' (Name=''); 
target property is 'NoTarget' (type 'Object') 

所以,如果我改变了绑定表达式"Path=DataContext.CurrentItem.CurveSpeedMustBeSpecified"它的工作列表框项目在一个DataTemplate基于父视图模型的属性风格但是只要父级用户控件的datacontext是BindingListCollectionView。这是不可接受的,因为用户控件的其余部分会自动绑定到BindingList上的CurrentItem的属性。

如何在样式中指定绑定表达式,以便它可以工作,而不管父数据上下文是集合视图还是单个项目?

回答

135

我在Silverlight中的相对源代码有问题。搜索和阅读后,我没有找到一个合适的解决方案,没有使用一些额外的绑定库。但是,这里是另一种通过直接引用您知道数据上下文的元素来访问父DataContext的方法。它采用Binding ElementName和工作得很好,只要你尊重自己的命名,并没有跨组件的templates/styles重重用:

<ItemsControl x:Name="level1Lister" ItemsSource={Binding MyLevel1List}> 
    <ItemsControl.ItemTemplate> 
    <DataTemplate> 
     <Button Content={Binding MyLevel2Property} 
       Command={Binding ElementName=level1Lister, 
         Path=DataContext.MyLevel1Command} 
       CommandParameter={Binding MyLevel2Property}> 
     </Button> 
    <DataTemplate> 
    <ItemsControl.ItemTemplate> 
</ItemsControl> 

如果你把按钮进入Style/Template这也适用:

<Border.Resources> 
    <Style x:Key="buttonStyle" TargetType="Button"> 
    <Setter Property="Template"> 
     <Setter.Value> 
     <ControlTemplate TargetType="Button"> 
      <Button Command={Binding ElementName=level1Lister, 
            Path=DataContext.MyLevel1Command} 
        CommandParameter={Binding MyLevel2Property}> 
       <ContentPresenter/> 
      </Button> 
     </ControlTemplate> 
     </Setter.Value> 
    </Setter> 
    </Style> 
</Border.Resources> 

<ItemsControl x:Name="level1Lister" ItemsSource={Binding MyLevel1List}> 
    <ItemsControl.ItemTemplate> 
    <DataTemplate> 
     <Button Content="{Binding MyLevel2Property}" 
       Style="{StaticResource buttonStyle}"/> 
    <DataTemplate> 
    <ItemsControl.ItemTemplate> 
</ItemsControl> 

起初我以为父元素的x:Names不是从模板项目中进行访问,但因为我没有找到更好的解决办法,我只是尝试,并能正常工作。

+0

我在我的项目中有这个确切的代码,但它泄漏ViewModels(终结者未调用,命令绑定似乎保留DataContext)。你能证实这个问题也存在吗? – 2013-01-22 11:05:21

+0

@Juve这个作品,但它可能做到这一点,以便它会触发实现相同模板的所有itemscontrol?名称是独一无二的,所以我们需要为每个模板分别设置一个模板,除非我错过了一些东西。 – Chris 2014-11-03 17:53:35

+1

@Juve无视我的最后一个,我通过使用relativeanceource和findancestor并通过祖先类型进行搜索(除非不按名称搜索,所有这些都一样)。在我的情况下,我重复使用ItemsControls每一个实现模板,所以我看起来像这样:Command =“{Binding RelativeSource = {RelativeSource FindAncestor,AncestorType = {x:Type ItemsControl}},Path = DataContext.OpenDocumentBtnCommand}” – Chris 2014-11-03 18:15:17

37

您可以使用RelativeSource寻父元素,这样的 -

Binding="{Binding Path=DataContext.CurveSpeedMustBeSpecified, 
RelativeSource={RelativeSource AncestorType={x:Type local:YourParentElementType}}}" 

详情请参阅this SO questionRelativeSource

+8

我不得不指定'Mode = FindAncestor'来使它工作,但是这样可以工作,并且在MVVM场景中更好,因为它避免了命名控制。 ''绑定=“{绑定路径= DataContext.CurveSpeedMustBeSpecified, RelativeSource = {RelativeSource Mode = FindAncestor,AncestorType = {x:Type local:YourParentElementType}}}'' – Aphex 2011-08-12 15:37:20

+1

像魅力<3一样工作,并且不必指定模式,.net 4.6.1 – user2475096 2017-12-01 22:00:04

16

我正在寻找如何做到在WPF类似的东西,我得到了这个解决方案:

<ItemsControl ItemsSource="{Binding MyItems,Mode=OneWay}"> 
<ItemsControl.ItemsPanel> 
    <ItemsPanelTemplate> 
     <StackPanel Orientation="Vertical" /> 
    </ItemsPanelTemplate> 
</ItemsControl.ItemsPanel> 
<ItemsControl.ItemTemplate> 
    <DataTemplate> 
     <RadioButton 
      Content="{Binding}" 
      Command="{Binding Path=DataContext.CustomCommand, 
         RelativeSource={RelativeSource Mode=FindAncestor,  
         AncestorType={x:Type ItemsControl}} }" 
      CommandParameter="{Binding}" /> 
    </DataTemplate> 
</ItemsControl.ItemTemplate> 

我希望这个作品为别人。我有一个自动设置为ItemsControls的数据上下文,并且此数据上下文具有两个属性:MyItems(它是一个集合)和一个命令'CustomCommand'。由于ItemTemplate正在使用DataTemplate,因此上层的DataContext不能直接访问。然后,获取父级的DC的解决方法是使用相对路径并按ItemsControl类型进行筛选。

14

RelativeSource vs.的ElementName

这两种方法可以实现相同的结果,

RelativeSrouce

Binding="{Binding Path=DataContext.MyBindingProperty, 
      RelativeSource={RelativeSource AncestorType={x:Type Window}}}" 

此方法查找一个类型窗口的控制(在本示例中),在视觉树,当它发现它时,你基本上可以使用Path=DataContext....访问它的DataContext。这种方法的优点是你不需要被绑定到一个名称上,它是一种动态的方式,但是对你的可视化树所做的改变会影响这个方法并且可能会破坏它。

的ElementName

Binding="{Binding Path=DataContext.MyBindingProperty, ElementName=MyMainWindow} 

这种方法referes以雄厚的静态Name所以只要你的范围可以看到它,你fine.You应该坚持你的命名约定不打破这种方法当然。这种方法非常简单,你只需要为你的Window/UserControl指定一个Name="..."

虽然所有三种类型(RelativeSource, Source, ElementName)都能够做同样的事情,但根据以下MSDN文章,每个更好地用于他们自己的专业领域。

How to: Specify the Binding Source

查找每个简要描述再加上页面底部的更多详细信息的链接一个表中。