web-dev-qa-db-fra.com

WPF ContextMenu malheurs: Comment définir le DataContext du ContextMenu?

J'ai du mal à trouver comment définir le DataContext correct sur un ContextMenu.

J'ai une collection de modèles de vues qui sont la source d'un ItemsControl. Chaque modèle de vue possède une collection d'éléments qui sont également la source d'un autre ItemsControl. Chaque élément est utilisé pour dessiner une image qui a un ContextMenu. Le MenuItems dans ce ContextMenu doit se lier à une commande sur le modèle de vue, mais le PlacementTarget du ContextMenu pointe vers l'élément individuel.

Mon Xaml ressemble à ceci:

<ItemsControl ItemsSource="{Binding Markers"}>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <ItemsControl ItemsSource="{Binding Items}">
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <Image>
                            <Image.ContextMenu>
                                <ContextMenu>
                                     <MenuItem Header="Edit" Command="{Binding EditCommand}" />
                                </ContextMenu>
                            </Image.ContextMenu>
                        </Image>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

Comment puis-je définir le DataContext du ContextMenu sur le modèle de vue parent correspondant de l'élément?

29
Ashley Grenon

Le ContextMenu est en dehors de l'arborescence visuelle. Voici le xaml qui devrait vous fournir le contexte de données:

<ItemsControl ItemsSource="{Binding Markers}" Tag="{Binding ElementName=outerControl, Path=DataContext}">
   ...
   <ContextMenu DataContext="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
      <MenuItem Header="Edit"
                Command="{Binding EditCommand}" />
   </ContextMenu>
   ...
</ItemsControl>

Ce post explique comment cela fonctionne.

43
kevindaub

Vous pouvez utiliser une extension de balisage:

using System;
using System.Windows.Controls;
using System.Windows.Markup;
using System.Xaml;

[MarkupExtensionReturnType(typeof(ContentControl))]
public class RootObject : MarkupExtension
{
    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        var rootObjectProvider = (IRootObjectProvider)serviceProvider.GetService(typeof(IRootObjectProvider));
        return rootObjectProvider?.RootObject;
    }
}

Il vous permet de faire:

<ItemsControl ItemsSource="{Binding Markers}">
   ...
   <ContextMenu DataContext="{Binding DataContext, Source={local:RootObject}}">
      <MenuItem Header="Edit"
                Command="{Binding EditCommand}" />
   </ContextMenu>
   ...
</ItemsControl>
16
Johan Larsson

Je n'aime pas utiliser Tag. Je préfère la propriété attenante.

Vous devez ajouter une propriété jointe:

public static readonly DependencyProperty DataContextExProperty = DependencyProperty.RegisterAttached("DataContextEx", typeof(Object), typeof(DependencyObjectAttached));

    public static Object GetDataContextEx(DependencyObject element)
    {
        return element.GetValue(DataContextExProperty);
    }

    public static void SetDataContextEx(DependencyObject element, Object value)
    {
        element.SetValue(DataContextExProperty, value);
    }

En XAML:

<Button attached:DependencyObjectAttached.DataContextEx="{Binding ElementName=MyDataContextElement, Path=DataContext}">
        <Button.ContextMenu>
            <ContextMenu DataContext="{Binding RelativeSource={RelativeSource Self}, Path=PlacementTarget.(attached:DependencyObjectAttached.DataContextEx)}">

            </ContextMenu>
        </Button.ContextMenu>
    </Button>
2
Smagin Alexey