Доступ к родительскому 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 автоматически.



как я могу указать выражение привязки внутри стиля, чтобы оно работало независимо от контекста родительских данных, являющегося представлением коллекции или одним элементом?

922   5  

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

    Ничего не найдено.