Доступ к родительскому DataContext из DataTemplate
у меня есть ListBox, который привязывается к дочерней коллекции в модели представления. Элементы списка оформлены в DataTemplate, основанного на собственность на родителя с 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" это работает, но только до тех пор, пока datacontext родительского пользовательского элемента управления является BindingListCollectionView. Это недопустимо, поскольку остальная часть пользовательского элемента управления привязывается к свойствам CurrentItem на BindingList автоматически.
как я могу указать выражение привязки внутри стиля, чтобы оно работало независимо от контекста родительских данных, являющегося представлением коллекции или одним элементом?
5 ответов:
у меня были проблемы с относительным источником в 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>это также работает, если вы поместите кнопку в
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родительских элементов не доступны из шаблонного элемента, но так как я не нашел лучшего решения, я просто попытался, и он отлично работает.
можно использовать
RelativeSourceчтобы найти родительский элемент, как это -Binding="{Binding Path=DataContext.CurveSpeedMustBeSpecified, RelativeSource={RelativeSource AncestorType={x:Type local:YourParentElementType}}}"посмотреть это так вопрос подробнее о
RelativeSource.
RelativeSource и ElementName
эти два подхода могут достичь одного и того же результата,
RelativeSrouce
Binding="{Binding Path=DataContext.MyBindingProperty, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"этот метод ищет элемент управления окна типа (в данном примере) в визуальном дереве, и когда он находит его, вы в основном можете получить доступ к нему
DataContextС помощьюPath=DataContext..... Плюсы этого метода в том, что вам не нужно привязываться к имени, и это своего рода динамический, однако изменения, внесенные в визуальное дерево, могут повлиять на этот метод и, возможно, нарушить его.ElementName
Binding="{Binding Path=DataContext.MyBindingProperty, ElementName=MyMainWindow}этот метод относится к твердому статическому
Nameтак что пока ваш прицел может видеть это, вы в порядке.Вы должны придерживаться своего соглашения об именах, чтобы не нарушать этот метод, конечно.Подход совсем простой и все, что вам нужно, это указатьName="..."для вашего окна / UserControl.хотя все три типы (
RelativeSource, Source, ElementName) способны делать то же самое, но согласно следующей статье MSDN, каждый из них лучше использовать в своей собственной области специальности.найти краткое описание каждого плюс ссылка на более подробную информацию в таблице в нижней части страницы.
Я искал, как сделать что-то подобное в 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из верхних уровней не является непосредственно доступным. Тогда решение для получения постоянного тока из родителей использовать относительный путь и фильтраItemsControlтип.
проблема в том, что DataTemplate не является частью элемента, к которому он применяется.
это означает, что если вы привязываетесь к шаблону, вы привязываетесь к чему-то, что не имеет контекста.
однако если вы поместите элемент внутри шаблона, то когда этот элемент применяется к родительскому он получает контекст и привязка затем работает
Так что это не будет работать
<DataTemplate > <DataTemplate.Resources> <CollectionViewSource x:Key="projects" Source="{Binding Projects}" >но это работает прекрасно
<DataTemplate > <GroupBox Header="Projects"> <GroupBox.Resources> <CollectionViewSource x:Key="projects" Source="{Binding Projects}" >потому что после datatemplate применяется groupbox помещается в родительский элемент и будет иметь доступ к его контексту
поэтому все, что вам нужно сделать, это удалить стиль из шаблона и переместить его в элемент в шаблоне
Примечание что контекст для itemscontrol-это элемент, а не элемент управления ie ComboBoxItem для ComboBox не сам ComboBox в этом случае вы должны использовать элементы управления ItemContainerStyle вместо
Comments