Access parent DataContext from Dataatemplate

j'ai un ListBox qui se lie à un enfant de la collection sur un ViewModel. Les éléments listbox sont stylisés dans un exemple de données basé sur une propriété sur le modèle de vue parent:

<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>

j'obtiens l'erreur de sortie suivante:

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')

donc si je change l'expression de la liaison à "Path=DataContext.CurrentItem.CurveSpeedMustBeSpecified" cela fonctionne, mais seulement aussi longtemps que le datacontexte du contrôle de l'utilisateur parent est un BindingListCollectionView . Ce n'est pas acceptable parce que le reste de la le contrôle de l'utilisateur se lie automatiquement aux propriétés du CurrentItem sur le BindingList .

Comment puis-je spécifier l'expression de liaison à l'intérieur du style pour qu'elle fonctionne indépendamment du fait que le contexte de données parent soit une vue de collecte ou un article unique?

90
demandé sur akjoshi 2010-08-04 14:42:32

5 réponses

J'ai eu des problèmes avec la source relative dans Silverlight. Après recherche et lecture, je n'ai pas trouvé de solution appropriée sans utiliser une bibliothèque de reliure supplémentaire. Mais, voici une autre approche pour accéder au DataContext parent en référençant directement un élément dont vous connaissez le contexte de données. Il utilise Binding ElementName et fonctionne très bien, tant que vous respectez votre propre appellation et ne pas avoir la réutilisation lourde de templates / styles à travers les composants:

<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>

cela fonctionne aussi si vous mettez le bouton dans 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>

au début j'ai pensé que le x:Names des éléments parents ne sont pas accessibles à partir d'un élément tempéré, mais comme je n'ai pas trouvé de meilleure solution, j'ai juste essayé, et il fonctionne très bien.

140
répondu Juve 2018-01-16 17:15:31

vous pouvez utiliser RelativeSource pour trouver l'élément parent, comme ceci -

Binding="{Binding Path=DataContext.CurveSpeedMustBeSpecified, 
RelativeSource={RelativeSource AncestorType={x:Type local:YourParentElementType}}}"

Voir cela DONC, la question pour plus de détails à propos de RelativeSource .

39
répondu akjoshi 2017-05-23 11:46:21

RelativeSource vs. ElementName

Ces deux approches, on peut obtenir le même résultat,

RelativeSrouce

Binding="{Binding Path=DataContext.MyBindingProperty, 
          RelativeSource={RelativeSource AncestorType={x:Type Window}}}"

cette méthode cherche une commande d'une fenêtre de type (dans cet exemple) dans l'arbre visuel et quand elle la trouve, Vous pouvez accéder à sa DataContext en utilisant la Path=DataContext.... . Le Les avantages de cette méthode est que vous n'avez pas besoin d'être lié à un nom et c'est une sorte de dynamique, cependant, les changements apportés à votre arbre visuel peut affecter cette méthode et peut-être la briser.

ElementName

Binding="{Binding Path=DataContext.MyBindingProperty, ElementName=MyMainWindow}

cette méthode se réfère à un solide statique Name aussi longtemps que votre champ de vision peut le voir, vous êtes très bien.Vous devriez vous en tenir à votre convention d'appellation pour ne pas briser cette méthode de cours.L'approche est simple et tout ce dont vous avez besoin est de spécifier un Name="..." pour votre fenêtre/UserControl.

bien que les trois types ( RelativeSource, Source, ElementName ) soient capables de faire la même chose, selon L'article suivant du MSDN, chacun devrait être utilisé dans son propre domaine de spécialité.

comment: préciser la source de liaison

trouver la brève description de chaque plus un lien à un plus détails dans le tableau en bas de la page.

21
répondu Mehrad 2015-10-01 22:27:56

je cherchais comment faire quelque chose de similaire dans WPF et j'ai eu cette 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>

j'espère que ça marchera pour quelqu'un d'autre. J'ai un contexte de données qui est défini automatiquement aux ItemsControls, et ce contexte de données a deux propriétés: MyItems -qui est une collection-, et une commande "CustomCommand". En raison du ItemTemplate utilise un DataTemplate , le DataContext des niveaux supérieurs n'est pas directement accessible. Ensuite, la solution pour obtenir le DC du parent est d'utiliser un chemin relatif et filtrer par le type ItemsControl .

16
répondu hmadrigal 2014-11-04 05:41:13

le problème est qu'un Dataemplate ne fait pas partie d'un élément qui lui est appliqué.

cela signifie que si vous vous liez au modèle, vous vous liez à quelque chose qui n'a pas de contexte.

cependant si vous mettez un élément à l'intérieur du modèle alors quand cet élément est appliqué au parent il gagne un contexte et la liaison fonctionne alors

donc ça ne marchera pas

<DataTemplate >
    <DataTemplate.Resources>
        <CollectionViewSource x:Key="projects" Source="{Binding Projects}" >

, mais cela fonctionne parfaitement

<DataTemplate >
    <GroupBox Header="Projects">
        <GroupBox.Resources>
            <CollectionViewSource x:Key="projects" Source="{Binding Projects}" >

parce qu'après l'application du dataemplate, le groupbox est placé dans le parent et aura accès à son contexte

donc tout ce que vous avez à faire est de supprimer le style du modèle et le déplacer dans un élément dans le modèle

note que le contexte pour un itemscontrol est l'élément le contrôle c'est à dire ComboBoxItem pour ComboBox pas le ComboBox lui-même, auquel cas vous devez utiliser le les contrôles ItemContainerStyle au lieu de

0
répondu MikeT 2018-06-08 13:16:50