从DataTemplate访问父DataContext

Mar*_*ius 101 data-binding wpf xaml relativesource datatemplate

我有一个ListBox绑定到ViewModel上的子集合.列表框项基于父ViewModel上的属性在datatemplate中设置样式:

<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>
Run Code Online (Sandbox Code Playgroud)

我收到以下输出错误:

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')
Run Code Online (Sandbox Code Playgroud)

因此,如果我将绑定表达式更改为"Path=DataContext.CurrentItem.CurveSpeedMustBeSpecified"它可以工作,但只要父用户控件的datacontext是a BindingListCollectionView.这是不可接受的,因为用户控件的其余部分会自动绑定到CurrentItemon的属性BindingList.

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

Juv*_*uve 149

我在Silverlight中遇到了相关来源的问题.搜索和阅读后,如果不使用其他一些Binding库,我找不到合适的解决方案.但是,这是另一种通过直接引用您知道数据上下文的元素来获取对父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>
Run Code Online (Sandbox Code Playgroud)

如果您将按钮放入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>
Run Code Online (Sandbox Code Playgroud)

起初我认为x:Names父元素不能从模板化的项目中访问,但由于我找不到更好的解决方案,我只是尝试了,它工作正常.


akj*_*shi 43

您可以使用它RelativeSource来查找父元素,如下所示 -

Binding="{Binding Path=DataContext.CurveSpeedMustBeSpecified, 
RelativeSource={RelativeSource AncestorType={x:Type local:YourParentElementType}}}"
Run Code Online (Sandbox Code Playgroud)

有关详细信息,请参阅此SO问题RelativeSource.

  • 我必须指定`Mode = FindAncestor`才能使用它,但是这在MVVM场景中工作得更好,因为它避免了命名控件.`Binding ="{Binding Path = DataContext.CurveSpeedMustBeSpecified,RelativeSource = {RelativeSource Mode = FindAncestor,AncestorType = {x:Type local:YourParentElementType}}}"` (10认同)

Meh*_*rad 28

RelativeSourceElementName

这两种方法可以达到相同的效果,

RelativeSrouce

Binding="{Binding Path=DataContext.MyBindingProperty, 
          RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
Run Code Online (Sandbox Code Playgroud)

这个方法寻找一个类型的窗口,在视觉树的控制(在这个例子中),当它找到你基本上可以访问它的DataContext使用Path=DataContext.....关于这种方法的优点是你不需要绑定名称而且它是动态的,但是,对可视树进行的更改可能会影响此方法并可能会破坏它.

的ElementName

Binding="{Binding Path=DataContext.MyBindingProperty, ElementName=MyMainWindow}
Run Code Online (Sandbox Code Playgroud)

这个方法引用了一个实体静态,Name所以只要你的范围可以看到它,你就没事了.你应该坚持你的命名惯例,当然不要破坏这种方法.方法很简单,你需要的只是指定一个Name="..."用于您的Window/UserControl.

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

如何:指定绑定源

查找每个的简要说明以及页面底部表格中更多详细信息的链接.


hma*_*gal 18

我正在搜索如何在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>
Run Code Online (Sandbox Code Playgroud)

我希望这适用于其他人.我有一个自动设置为ItemsControls的数据上下文,这个数据上下文有两个属性:MyItems-which是collection-,还有一个命令'CustomCommand'.由于ItemTemplate使用a DataTemplate,DataContext上层不可直接访问.然后获取父级DC的变通方法是使用相对路径并按ItemsControl类型过滤.