Access parent DataContext from DataTemplate
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:
<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.
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?
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 DataContext by 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:
<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
:
<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.
You can use RelativeSource
to find the parent element, like this -
Binding="{Binding Path=DataContext.CurveSpeedMustBeSpecified,
RelativeSource={RelativeSource AncestorType={x:Type local:YourParentElementType}}}"
See this SO question for more details about RelativeSource
.
I was searching how to do something similar in WPF and I got this solution:
<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.