66 votes

Liaison entre le nom de l'élément et l'élément de menu dans le menu contextuel

Quelqu'un d'autre a-t-il remarqué que les liaisons avec ElementName ne sont pas résolues correctement dans le cas de MenuItem qui sont contenus dans ContextMenu objets ? Regardez cet échantillon :

<Window x:Class="EmptyWPF.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300"
    x:Name="window">
    <Grid x:Name="grid" Background="Wheat">
        <Grid.ContextMenu>
            <ContextMenu x:Name="menu">
                <MenuItem x:Name="menuItem" Header="Window" Tag="{Binding ElementName=window}" Click="MenuItem_Click"/>
                <MenuItem Header="Grid" Tag="{Binding ElementName=grid}" Click="MenuItem_Click"/>
                <MenuItem Header="Menu" Tag="{Binding ElementName=menu}" Click="MenuItem_Click"/>
                <MenuItem Header="Menu Item" Tag="{Binding ElementName=menuItem}" Click="MenuItem_Click"/>
            </ContextMenu>
        </Grid.ContextMenu>
        <Button Content="Menu" 
                HorizontalAlignment="Center" VerticalAlignment="Center" 
                Click="MenuItem_Click" Tag="{Binding ElementName=menu}"/>
        <Menu HorizontalAlignment="Center" VerticalAlignment="Bottom">
            <MenuItem x:Name="anotherMenuItem" Header="Window" Tag="{Binding ElementName=window}" Click="MenuItem_Click"/>
            <MenuItem Header="Grid" Tag="{Binding ElementName=grid}" Click="MenuItem_Click"/>
            <MenuItem Header="Menu" Tag="{Binding ElementName=menu}" Click="MenuItem_Click"/>
            <MenuItem Header="Menu Item" Tag="{Binding ElementName=anotherMenuItem}" Click="MenuItem_Click"/>
        </Menu>
    </Grid>
</Window>

Toutes les liaisons fonctionnent parfaitement, à l'exception des liaisons contenues dans le ContextMenu. Ils affichent une erreur dans la fenêtre de sortie pendant l'exécution.

Quelqu'un connaît-il des solutions de contournement ? Qu'est-ce qui se passe ici ?

56voto

Josh G Points 7547

J'ai trouvé une solution beaucoup plus simple.

Dans le code derrière pour le UserControl :

NameScope.SetNameScope(contextMenu, NameScope.GetNameScope(this));

20voto

Will Points 76760

Voici une autre solution de contournement pour xaml uniquement. (Cela suppose également que vous voulez ce qui se trouve à l'intérieur de la balise DataContext par exemple, vous êtes MVVMing il)

Première option, où l'élément parent de l'élément Menu contextuel n'est pas dans un DataTemplate :

Command="{Binding PlacementTarget.DataContext.MyCommand, 
         RelativeSource={RelativeSource AncestorType=ContextMenu}}"

Cela conviendrait à la question de l'OP. Cela ne fonctionnera pas si vous êtes à l'intérieur d'un fichier DataTemplate . Dans ces cas, le DataContext est souvent l'un des nombreux éléments d'une collection, et l'élément ICommand à laquelle vous souhaitez vous lier est une propriété sœur de la collection dans le même ViewModel (l'attribut DataContext de la fenêtre, par exemple).

Dans ces cas, vous pouvez profiter de l'aide de la Étiquette pour retenir temporairement le parent DataContext qui contient à la fois la collection ET votre ICommand :

class ViewModel
{
    public ObservableCollection<Derp> Derps { get;set;}
    public ICommand DeleteDerp {get; set;}
} 

et dans le xaml

<!-- ItemsSource binds to Derps in the DataContext -->
<StackPanel
    Tag="{Binding DataContext, ElementName=root}">
    <StackPanel.ContextMenu>
        <ContextMenu>
            <MenuItem
                Header="Derp"                       
                Command="{Binding PlacementTarget.Tag.DeleteDerp, 
                RelativeSource={RelativeSource 
                                    AncestorType=ContextMenu}}"
                CommandParameter="{Binding PlacementTarget.DataContext, 
                RelativeSource={RelativeSource AncestorType=ContextMenu}}">
            </MenuItem>

5voto

Josh Points 153

Il est difficile de lier les menus contextuels. Ils existent en dehors de l'arbre visuel de votre contrôle, et ne peuvent donc pas trouver le nom de votre élément.

Essayez de définir le datacontext de votre menu contextuel à sa cible de placement. Vous devez utiliser RelativeSource.

<ContextMenu 
   DataContext="{Binding PlacementTarget, RelativeSource={RelativeSource Self}}"> ...

4voto

Josh G Points 7547

Après avoir expérimenté un peu, j'ai découvert une solution de contournement :

Rendre le niveau supérieur Window / UserControl mettre en œuvre INameScope et mettre NameScope de ContextMenu au contrôle de niveau supérieur.

public class Window1 : Window, INameScope
{
    public Window1()
    {
        InitializeComponent();
        NameScope.SetNameScope(contextMenu, this);
    }

    // Event handlers and etc...

    // Implement INameScope similar to this:
    #region INameScope Members

    Dictionary<string, object> items = new Dictionary<string, object>();

    object INameScope.FindName(string name)
    {
        return items[name];
    }

    void INameScope.RegisterName(string name, object scopedElement)
    {
        items.Add(name, scopedElement);
    }

    void INameScope.UnregisterName(string name)
    {
        items.Remove(name);
    }

    #endregion
}

Cela permet au menu contextuel de trouver des éléments nommés à l'intérieur de l'arborescence de l'UE. Window . D'autres options ?

1voto

Marino Šimić Points 4885

Je ne vois pas pourquoi recourir à des tours de magie juste pour éviter une ligne de code dans l'eventhandler pour le clic de souris que vous gérez déjà :

    private void MenuItem_Click(object sender, System.Windows.RoutedEventArgs e)
    {
        // this would be your tag - whatever control can be put as string intot he tag
        UIElement elm = Window.GetWindow(sender as MenuItem).FindName("whatever control") as UIElement;
    }

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X