121

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

<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"它可以工作,但只要父用户控件的数据上下文是BindingListCollectionView. 这是不可接受的,因为用户控件的其余部分会自动绑定到CurrentItemon 的属性BindingList

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

4

6 回答 6

169

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

如果您将按钮放入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无法从模板项中访问父元素,但由于我没有找到更好的解决方案,我只是尝试了一下,它工作正常。

于 2010-08-23T12:55:32.690 回答
53

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

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

有关. _ _RelativeSource

于 2010-08-04T12:27:25.173 回答
38

RelativeSourceElementName

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

相对来源

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

此方法在可视化树中查找类型为 Window(在此示例中)的控件,当找到它时,您基本上可以DataContext使用Path=DataContext..... 这种方法的优点是你不需要绑定一个名字,而且它是动态的,但是,对你的可视化树所做的更改可能会影响这个方法并可能破坏它。

元素名称

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

这个方法是指一个固定的静态Name,只要你的范围能看到就可以了。你应该坚持你的命名约定,当然不要破坏这个方法。这个方法很简单,你只需要指定aName="..."用于您的窗口/用户控件。

虽然所有三种类型 ( RelativeSource, Source, ElementName) 都能够做同样的事情,但根据下面的 MSDN 文章,最好在各自的专业领域中使用每种类型。

如何:指定绑定源

在页面底部的表格中找到每个的简要说明以及指向更多详细信息的链接。

于 2015-03-05T02:17:27.567 回答
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>

我希望这对其他人有用。我有一个自动设置为 ItemsControls 的数据上下文,该数据上下文有两个属性:MyItems-它是一个集合-和一个命令“CustomCommand”。由于ItemTemplateis 使用 a DataTemplateDataContext上层的 the 不能直接访问。然后获取父级 DC 的解决方法是使用相对路径并按ItemsControl类型过滤。

于 2011-06-03T00:54:41.683 回答
0

问题是 DataTemplate 不是应用于它的元素的一部分。

这意味着如果您绑定到模板,您将绑定到没有上下文的东西。

但是,如果您将一个元素放在模板中,那么当该元素应用于父元素时,它会获得一个上下文,然后绑定就会起作用

所以这行不通

<DataTemplate >
    <DataTemplate.Resources>
        <CollectionViewSource x:Key="projects" Source="{Binding Projects}" >

但这完美无缺

<DataTemplate >
    <GroupBox Header="Projects">
        <GroupBox.Resources>
            <CollectionViewSource x:Key="projects" Source="{Binding Projects}" >

因为在应用数据模板后,组框被放置在父级中并且可以访问其上下文

所以您所要做的就是从模板中删除样式并将其移动到模板中的元素中

请注意,项目控件的上下文是项目而不是控件,即 ComboBox 的 ComboBoxItem 不是 ComboBox 本身,在这种情况下,您应该改用控件 ItemContainerStyle

于 2018-06-08T12:54:56.360 回答
0

ElementName=Something是的,您可以使用Juve 建议的方法来解决它。

但!

如果子元素(您在其上使用这种绑定)是一个用户控件,它使用与您在父控件中指定的元素名称相同的元素名称,那么绑定会转到错误的对象!

我知道这篇文章不是解决方案,但我认为在绑定中使用 ElementName 的每个人都应该知道这一点,因为它可能是运行时错误。

<UserControl x:Class="MyNiceControl"
             x:Name="TheSameName">
   the content ...
</UserControl>

<UserControl x:Class="AnotherUserControl">
        <ListView x:Name="TheSameName">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <MyNiceControl Width="{Binding DataContext.Width, ElementName=TheSameName}" />
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
</UserControl>
于 2019-12-18T13:58:12.260 回答