name - wpf datacontext binding relativesource




Accéder au parent DataContext à partir de DataTemplate (4)

J'ai eu des problèmes avec la source relative dans Silverlight. Après la recherche et la lecture, je n'ai pas trouvé de solution appropriée sans utiliser une bibliothèque de liaison 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, du moment que vous respectez votre propre nom et que vous ne réutilisez pas trop les templates / styles dans 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 également 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, je pensais que les x:Names des éléments parents ne sont pas accessibles à partir d'un élément structuré, mais comme je n'ai pas trouvé de meilleure solution, j'ai juste essayé, et ça marche bien.

J'ai un ListBox qui lie à une collection enfant sur un ViewModel. Les éléments de la liste sont stylisés dans un datatemplate basé sur une propriété du ViewModel 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>

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 change l'expression de liaison en "Path=DataContext.CurrentItem.CurveSpeedMustBeSpecified" cela fonctionne, mais seulement tant que le datacontext du contrôle utilisateur parent est un BindingListCollectionView . Cela n'est pas acceptable car le reste du contrôle utilisateur se lie automatiquement aux propriétés de CurrentItem sur BindingList .

Comment puis-je spécifier l'expression de liaison dans le style afin qu'il fonctionne indépendamment du contexte de données parent étant une vue de collection ou un seul élément?


Je cherchais à 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 cela fonctionne pour quelqu'un d'autre. J'ai un contexte de données qui est défini automatiquement à ItemsControls, et ce contexte de données a deux propriétés: MyItems - qui est une collection -, et une commande 'CustomCommand'. Parce que ItemTemplate utilise un DataTemplate , le DataContext des niveaux supérieurs n'est pas directement accessible. La solution de contournement pour obtenir le contrôleur de domaine du parent est ensuite utiliser un chemin relatif et filtrer par le type ItemsControl .


le problème est qu'un DataTemplate ne fait pas partie d'un élément qui lui 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 mettez un élément dans le template, 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}" >

car après l'application du datatemplate, la zone de groupe est placée dans le parent et aura accès à son contexte

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

Notez que le contexte d'un itemscontrol est l'élément qui n'est pas le contrôle, c'est-à-dire ComboBoxItem pour ComboBox et non le ComboBox lui-même, auquel cas vous devriez utiliser les contrôles ItemContainerStyle à la place


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'une fenêtre de type (dans cet exemple) dans l'arborescence visuelle et quand il la trouve, vous pouvez accéder à DataContext utilisant le Path=DataContext.... Les Pros à propos de cette méthode est que vous n'avez pas besoin d'être lié à un nom et c'est un peu dynamique, cependant, les changements apportés à votre arbre visuel 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, aussi longtemps que votre portée peut le voir, tout va bien. Vous devriez respecter votre convention de nommage pour ne pas casser cette méthode. L'approche est simple et tout ce dont vous avez besoin est de spécifiez un Name="..." pour votre Window / UserControl.

Bien que tous les trois types ( RelativeSource, Source, ElementName ) sont capables de faire la même chose, mais selon l'article MSDN suivant, chacun devrait être mieux 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.







relativesource