我有一个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>
我收到以下输出错误:
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"
它可以工作,但只要父用户控件的datacontext是BindingListCollectionView
。这是不可接受的,因为用户控件的其余部分会自动绑定CurrentItem
上BindingList
的属性。
如何在样式中指定绑定表达式,使其无论父数据上下文是集合视图还是单个项目都可以工作?
答案 0 :(得分:141)
我在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
无法从模板化项目中访问,但由于我找不到更好的解决方案,我只是尝试了,它运行正常。
答案 1 :(得分:41)
您可以使用RelativeSource
查找父元素,如下所示 -
Binding="{Binding Path=DataContext.CurveSpeedMustBeSpecified,
RelativeSource={RelativeSource AncestorType={x:Type local:YourParentElementType}}}"
有关RelativeSource
的详细信息,请参阅this SO question。
答案 2 :(得分:25)
RelativeSource 与 ElementName
这两种方法可以达到相同的效果,
<强> RelativeSrouce 强>
Binding="{Binding Path=DataContext.MyBindingProperty,
RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
此方法在可视树中查找类型Window(在此示例中)的控件,当它找到它时,您基本上可以使用DataContext
访问它Path=DataContext....
。关于这种方法的优点是你不需要绑定名称而且它是动态的,但是,对可视化树所做的更改会影响这种方法并可能会破坏它。
<强> 的ElementName 强>
Binding="{Binding Path=DataContext.MyBindingProperty, ElementName=MyMainWindow}
这个方法引用一个固定的静态Name
,所以只要你的范围可以看到它,你就没事了。你应该坚持你的命名惯例当然不要破坏这种方法。方法是qute简单而且您只需要为Window / UserControl指定Name="..."
。
尽管所有三种类型(RelativeSource, Source, ElementName
)都能够做同样的事情,但根据以下MSDN文章,每个类型最好用于他们自己的专业领域。
How to: Specify the Binding Source
查找每个的简要说明以及页面底部表格中更多详细信息的链接。
答案 3 :(得分:17)
我正在寻找如何在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
类型过滤。
答案 4 :(得分: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}" >
因为在应用了数据窗口之后,组框被放置在父框架中并且可以访问其上下文
所以你要做的就是从模板中删除样式并将其移动到模板中的元素
注意,itemscontrol的上下文是项而不是控件 即ComboBox的ComboBoxItem而不是ComboBox本身,在这种情况下你应该使用控件ItemContainerStyle而不是
答案 5 :(得分:0)
是的,您可以按照Juve的建议使用ElementName=Something
解决问题。
但是!
如果子元素(在其上使用这种绑定的元素)是用户控件,且其使用的名称与您在父控件中指定的元素名称相同,则绑定将转到错误的对象!
我知道这篇文章不是解决方案,但是我认为在绑定中使用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>