Accéder au parent DataContext à partir de DataTemplate

J’ai un ListBox qui se lie à une collection enfant sur un ViewModel. Les éléments de la zone de liste sont stylés dans un modèle de données basé sur une propriété du composant ViewModel parent:

        

Je reçois 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 modifie l’expression de liaison en "Path=DataContext.CurrentItem.CurveSpeedMustBeSpecified" cela fonctionne, mais seulement tant que le contexte de données du contrôle utilisateur parent est un BindingListCollectionView . Cela n’est pas acceptable car le rest du contrôle utilisateur se lie automatiquement aux propriétés du CurrentItem sur le BindingList .

Comment puis-je spécifier l’expression de liaison dans le style afin qu’elle fonctionne quel que soit le contexte de données parent qui est une vue de collection ou un élément unique?

J’ai eu des problèmes avec la source relative dans Silverlight. Après avoir cherché et lu, 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 assez bien, à condition que vous respectiez votre propre dénomination et que vous n’utilisiez pas beaucoup de templates / styles travers les composants:

        

Cela fonctionne également si vous placez le bouton dans Style / Template :

           

Au début, je pensais que les x:Names des éléments parents ne sont pas accessibles depuis un élément basé sur un modèle, mais comme je n’ai pas trouvé de meilleure solution, j’ai juste essayé, et ça fonctionne bien.

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 cette question SO pour plus de détails sur RelativeSource .

RelativeSource vs. ElementName

Ces deux approches peuvent aboutir au même résultat,

RelativeSrouce

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

Cette méthode recherche un contrôle d’un type Window (dans cet exemple) dans l’arborescence visuelle et quand il la trouve, vous pouvez accéder à son DataContext utilisant le Path=DataContext.... Les avantages de cette méthode sont que vous n’avez pas besoin d’être lié à un nom et que c’est un peu dynamic, cependant, les modifications apscopes à votre arborescence visuelle peuvent affecter cette méthode et éventuellement la casser.

ElementName

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

Cette méthode se réfère à un Name statique solide pour autant que votre scope puisse le voir, vous êtes bien. Vous devriez vous en tenir à votre convention de nommage pour ne pas casser cette méthode bien sûr. L’approche est très simple et tout ce dont vous avez besoin est de spécifiez un Name="..." pour votre Window / UserControl.

Bien que les trois types ( RelativeSource, Source, ElementName ) soient capables de faire la même chose, mais selon l’article MSDN suivant, chacun devrait être utilisé dans leur propre domaine de spécialité.

Comment: spécifier la source de liaison

Trouvez la brève description de chacun plus un lien vers un plus de détails dans le tableau au bas de la page.

Je cherchais comment faire quelque chose de similaire dans WPF et j’ai eu cette solution:

            

J’espère que cela fonctionne pour quelqu’un d’autre. J’ai un contexte de données qui est défini automatiquement sur ItemsControls, et ce contexte de données a deux propriétés: MyItems qui est une collection, et une commande CustomCommand. Étant donné que ItemTemplate utilise un DataTemplate , le DataContext des niveaux supérieurs n’est pas directement accessible. Ensuite, la solution de contournement pour obtenir le contrôleur de domaine du parent est d’utiliser un chemin d’access et un filtre relatifs par type ItemsControl .

le problème est qu’un DataTemplate ne fait pas partie d’un élément auquel il est appliqué.

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

Cependant, si vous placez un élément dans le modèle, lorsque cet élément est appliqué au parent, il gagne un contexte et la liaison fonctionne alors

donc ça ne marchera pas

    

mais cela fonctionne parfaitement

     

car après l’application du modèle de données, la boîte de groupe est placée dans le parent et aura access à son contexte

il suffit donc de supprimer le style du modèle et de le déplacer dans un élément du modèle.

notez que le contexte pour un itemscontrol est l’élément pas le contrôle, c.-à-d. ComboBoxItem pour ComboBox pas le ComboBox lui-même, auquel cas vous devez utiliser les contrôles ItemContainerStyle à la place