wpf 从 DataTemplate 访问父 DataContext
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3404707/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Access parent DataContext from DataTemplate
提问by Marius
I have a ListBox
which binds to a child collection on a ViewModel. The listbox items are styled in a datatemplate based on a property on the parent ViewModel:
我有一个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>
I get the following output error:
我收到以下输出错误:
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')
So if I change the the binding expression to "Path=DataContext.CurrentItem.CurveSpeedMustBeSpecified"
it works, but only as long as the datacontext of the parent user control is a BindingListCollectionView
. This is not acceptable because the rest of the user control binds to the properties of the CurrentItem
on the BindingList
automatically.
因此,如果我将绑定表达式更改为"Path=DataContext.CurrentItem.CurveSpeedMustBeSpecified"
有效,但前提是父用户控件的数据上下文是BindingListCollectionView
. 这是不能接受的,因为用户控件绑定到的属性的其余部分CurrentItem
上的BindingList
自动。
How can I specify the binding expression inside the style so that it works regardless of the parent data context being a collection view or a single item?
如何在样式中指定绑定表达式,以便无论父数据上下文是集合视图还是单个项目,它都可以工作?
回答by Juve
I had problems with the relative source in Silverlight. After searching and reading I did not find a suitable solution without using some additional Binding library. But, here is another approach for gaining access to the parent DataContextby directly referencing an element of which you know the data context. It uses Binding ElementName
and works quite well, as long as you respect your own naming and don't have heavy reuse of templates
/styles
across components:
我在 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>
This also works if you put the button into Style
/Template
:
如果您将按钮放入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>
At first I thought that the x:Names
of parent elements are not accessible from within a templated item, but since I found no better solution, I just tried, and it works fine.
起初我认为x:Names
父元素的of 不能从模板化项目中访问,但由于我没有找到更好的解决方案,我只是尝试过,它工作正常。
回答by akjoshi
You can use RelativeSource
to find the parent element, like this -
您可以使用RelativeSource
来查找父元素,如下所示 -
Binding="{Binding Path=DataContext.CurveSpeedMustBeSpecified,
RelativeSource={RelativeSource AncestorType={x:Type local:YourParentElementType}}}"
See this SO questionfor more details about RelativeSource
.
见这太问题有关的详细信息RelativeSource
。
回答by Mehrad
RelativeSourcevs. ElementName
相对来源与元素名称
These two approaches can achieve the same result,
这两种方法可以达到相同的结果,
RelativeSrouce
相对源
Binding="{Binding Path=DataContext.MyBindingProperty,
RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
This method looks for a control of a type Window (in this example) in the visual tree and when it finds it you basically can access it's DataContext
using the Path=DataContext....
. The Pros about this method is that you don't need to be tied to a name and it's kind of dynamic, however, changes made to your visual tree can affect this method and possibly break it.
此方法在可视化树中查找类型为 Window(在此示例中)的控件,当它找到时,您基本上可以DataContext
使用Path=DataContext....
. 这种方法的优点是您不需要绑定到名称,而且它是动态的,但是,对可视化树所做的更改可能会影响此方法并可能破坏它。
ElementName
元素名称
Binding="{Binding Path=DataContext.MyBindingProperty, ElementName=MyMainWindow}
This method referes to a solid static Name
so as long as your scope can see it, you're fine.You should be sticking to your naming convention not to break this method of course.The approach is qute simple and all you need is to specify a Name="..."
for your Window/UserControl.
这个方法引用了一个可靠的静态,Name
所以只要你的范围可以看到它,你就可以了。当然,你应该坚持你的命名约定,不要破坏这个方法。这个方法很简单,你只需要指定aName="..."
用于您的 Window/UserControl。
Although all three types (RelativeSource, Source, ElementName
) are capable of doing the same thing, but according to the following MSDN article, each one better be used in their own area of specialty.
虽然这三种类型 ( RelativeSource, Source, ElementName
) 都能够做同样的事情,但根据下面的 MSDN 文章,每一种都最好用于自己的专业领域。
How to: Specify the Binding Source
Find the brief description of each plus a link to a more details one in the table on the bottom of the page.
在页面底部的表格中找到每项的简要说明以及指向更多详细信息的链接。
回答by hmadrigal
I was searching how to do something similar in WPF and I got this solution:
我正在寻找如何在 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>
I hope this works for somebody else. I have a data context which is set automatically to the ItemsControls, and this data context has two properties: MyItems
-which is a collection-, and one command 'CustomCommand'. Because of the ItemTemplate
is using a DataTemplate
, the DataContext
of upper levels is not directly accessible. Then the workaround to get the DC of the parent is use a relative path and filter by ItemsControl
type.
我希望这对其他人有用。我有一个自动设置为 ItemsControls 的数据上下文,该数据上下文有两个属性:MyItems
-这是一个集合-和一个命令“CustomCommand”。由于ItemTemplate
使用的是 a DataTemplate
,DataContext
不能直接访问上层。然后获取父级 DC 的解决方法是使用相对路径并按ItemsControl
类型过滤。
回答by MikeT
the issue is that a DataTemplate isn't part of an element its applied to it.
问题是 DataTemplate 不是应用于它的元素的一部分。
this means if you bind to the template you're binding to something that has no context.
这意味着如果您绑定到模板,您将绑定到没有上下文的内容。
however if you put a element inside the template then when that element is applied to the parent it gains a context and the binding then works
但是,如果您将一个元素放在模板中,那么当该元素应用于父元素时,它会获得一个上下文,然后绑定就可以工作了
so this will not work
所以这行不通
<DataTemplate >
<DataTemplate.Resources>
<CollectionViewSource x:Key="projects" Source="{Binding Projects}" >
but this works perfectly
但这很有效
<DataTemplate >
<GroupBox Header="Projects">
<GroupBox.Resources>
<CollectionViewSource x:Key="projects" Source="{Binding Projects}" >
because after the datatemplate is applied the groupbox is placed in the parent and will have access to its Context
因为在应用数据模板后,分组框被放置在父级中并且可以访问其上下文
so all you have to do is remove the style from the template and move it into an element in the template
所以你所要做的就是从模板中删除样式并将其移动到模板中的元素中
notethat the context for a itemscontrol is the item not the control ie ComboBoxItem for ComboBox not the ComboBox itself in which case you should use the controls ItemContainerStyle instead
请注意, itemscontrol 的上下文是项目而不是控件,即 ComboBox 的 ComboBoxItem 不是 ComboBox 本身,在这种情况下,您应该使用控件 ItemContainerStyle
回答by Lumo
Yes, you can solve it using the ElementName=Something
as suggested by Juve.
是的,您可以使用ElementName=Something
Juve 建议的方法解决它。
BUT!
但!
If a child element (on which you use this kind of binding) is a user control which uses the same element name as you specify in the parent control, then the binding goes to the wrong object!!
如果子元素(您在其上使用这种绑定)是使用与您在父控件中指定的元素名称相同的元素名称的用户控件,那么绑定将转到错误的对象!
I know this post is not a solution but I thought everyone who uses the ElementName in the binding should know this, since it's a possible runtime bug.
我知道这篇文章不是一个解决方案,但我认为在绑定中使用 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>