wpf relativesource ancestorlevel




Liaison RelativeSource à partir d'une info-bulle ou d'un menu contextuel (3)

Étant ContextMenu que ContextMenu n'est pas dans l'arborescence visuelle, la liaison ne fonctionnera pas. une solution simple utilise Proxy Pattern, vous pouvez créer une classe wrapper qui hérite de DependencyObject et qui possède un DependencyProperty qui conservera un DataContext de votre Window , puis vous pouvez avoir une ressource du proxy dans XAML et lier enfin votre commande MenuItem à votre commande souhaitée via l'objet proxy.
Sample Proxy:

Public class ProxyClass : DependencyObject
{
    Public object Data {get; set;}
   public static readonly DependencyProperty DataProperty = DependencyProperty.Register("DataProperty", typeof(object), typeof(ProxyClass), new FrameworkPropertyMetadata(null));

}

Comment utiliser dans XAML:

<Window DataContext="{Binding MyViewModel}">
...
<Window.Resources>
    <ProxyClass Data={Binding} x:Key="BindingProxy"/>

</Window.Resources>
...  
<MenuItem Command="{Binding Source={StaticResource BindingProxy}, Path=Data.MyDesiredCommand"/>
...
</Window>

Qu'est-ce qui se passe?
Data propriété Data de ProxyClass sera ProxyClass à DataContext de Window , puis elle aura toutes vos propriétés et propriétés de votre ViewModel dans la ressource ProxyClass .
Un autre avantage de cette approche est la portabilité et la réutilisation dans plusieurs vues et projets.

Qu'est-ce que je fais mal ici?:

 <GridViewColumn>
    <GridViewColumn.CellTemplate>
       <DataTemplate>
          <Button>
            <Button.ToolTip>
              <TextBlock Text="{Binding Path=Title, RelativeSource={RelativeSource AncestorType=Window}}" />

C'est juste un exemple simplifié, qui ne fonctionne pas du tout :) En fait, je dois obtenir une valeur d'une autre propriété qui est dans la portée du DataContext de Window.

Aidez-moi à pls.


Cela est délicat car ToolTip ne fait pas partie de VisualTree. Here vous voyez une solution intéressante pour le même problème avec ContextMenus. De la même manière que vous pouvez aller pour l'info-bulle.

METTRE À JOUR
Malheureusement, le lien a disparu et je n'ai plus trouvé l'article référencé.
Pour autant que je m'en souvienne, le blog référencé a montré comment se lier à un DataContext d'un autre VisualTree, ce qui est souvent nécessaire lors de la liaison depuis un ToolTip, un ContextMenu ou un Popup.

Une bonne façon de faire est de fournir l'instance souhaitée (par exemple, ViewModel) dans la propriété Tag du PlacementTarget. L'exemple suivant le fait pour accéder à une instance de commande d'un ViewModel:

<Button Tag="{Binding DataContext,RelativeSource={RelativeSource Mode=Self}}">
  <Button.ContextMenu>
    <ContextMenu>
       <MenuItem Command="{Binding PlacementTarget.Tag.DesiredCommand,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ContextMenu}}" .../>
    <ContextMenu>
  </Button.ContextMenu>
</Button>

Je ne l'ai pas testé et c'est la dernière fois que je l'ai fait. S'il vous plaît faire un commentaire si cela ne fonctionne pas pour vous.

MISE À JOUR 2

Comme le lien original sur lequel cette réponse a été écrite a disparu, j'ai consulté archive.org et trouvé l'entrée de blog originale . La voici, mot pour mot sur le blog:

Étant donné qu'un ContextMenu dans WPF n'existe pas dans l'arborescence visuelle de votre page / fenêtre / contrôle en soi, la liaison de données peut être un peu délicate. J'ai cherché haut et bas sur le web pour cela, et la réponse la plus commune semble être "juste le faire dans le code derrière". FAUX! Je ne suis pas venu dans le monde merveilleux de XAML pour revenir à des choses dans le code.

Voici mon exemple qui vous permettra de vous connecter à une chaîne qui existe en tant que propriété de votre fenêtre.

public partial class Window1 : Window
{
    public Window1()
    {
        MyString = "Here is my string";
    }

    public string MyString
    {
        get;
        set;

    }
}


<Button Content="Test Button" 
     Tag="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}}">
  <Button.ContextMenu>
    <ContextMenu DataContext="{Binding Path=PlacementTarget.Tag, 
          RelativeSource={RelativeSource Self}}" >
      <MenuItem Header="{Binding MyString}"/>
    </ContextMenu>
  </Button.ContextMenu>   
</Button>

La partie importante est le tag sur le bouton (bien que vous puissiez tout aussi facilement définir le DataContext du bouton). Cela stocke une référence à la fenêtre parente. Le ContextMenu est capable d'accéder à ceci via sa propriété PlacementTarget. Vous pouvez ensuite transmettre ce contexte à travers vos éléments de menu.

J'admets que ce n'est pas la solution la plus élégante au monde. Cependant, il bat les paramètres dans le code derrière. Si quelqu'un a une meilleure façon de faire cela, j'adorerais l'entendre.


Par ci-dessous:
PlacementTarget est le contrôle propriétaire du ContextMenu (ex: DataGrid). Pas besoin d'une propriété "tag".

IsEnabled se lie à la valeur "myProperty" du DataGrid.

J'ai testé cela et ça marche. A eu un problème similaire avec la liaison.

<ContextMenu
DataContext="{Binding Path=PlacementTarget, RelativeSource={RelativeSource Self}}"
IsEnabled="{Binding myProperty}"  
>




relativesource