Comment définir CommandTarget pour MenuItem à l'intérieur d'un ContextMenu?

(Cette question est liée à un autre, mais assez différent, je pense qu'il garantit le placement ici.)

Voici un (très ciselée) Window:

<Window x:Class="Gmd.TimeTracker2.TimeTrackerMainForm"
    xmlns:local="clr-namespace:Gmd.TimeTracker2"
    xmlns:localcommands="clr-namespace:Gmd.TimeTracker2.Commands"
    x:Name="This"
    DataContext="{Binding ElementName=This}">
    <Window.CommandBindings>
        <CommandBinding Command="localcommands:TaskCommands.ViewTaskProperties" 
                        Executed="HandleViewTaskProperties" 
                        CanExecute="CanViewTaskPropertiesExecute" />
    </Window.CommandBindings>
    <DockPanel>
<!-- snip stuff -->
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
<!-- snip more stuff -->
            <Button Content="_Create a new task" Grid.Row="1" x:Name="btnAddTask" Click="HandleNewTaskClick" />
        </Grid>
    </DockPanel>
</Window>

et voici un (très ciselée) UserControl:

<UserControl x:Class="Gmd.TimeTracker2.TaskStopwatchControl"
             xmlns:local="clr-namespace:Gmd.TimeTracker2"
             xmlns:localcommands="clr-namespace:Gmd.TimeTracker2.Commands"
             x:Name="This"
             DataContext="{Binding ElementName=This}">
    <UserControl.ContextMenu>
        <ContextMenu>
            <MenuItem x:Name="mnuProperties" Header="_Properties" Command="{x:Static localcommands:TaskCommands.ViewTaskProperties}" 
                      CommandTarget="What goes here?" />
        </ContextMenu>
    </UserControl.ContextMenu>
    <StackPanel>
        <TextBlock MaxWidth="100" Text="{Binding Task.TaskName, Mode=TwoWay}" TextWrapping="WrapWithOverflow" TextAlignment="Center" />
        <TextBlock Text="{Binding Path=ElapsedTime}" TextAlignment="Center" />
        <Button Content="{Binding Path=IsRunning, Converter={StaticResource boolToString}, ConverterParameter='Stop Start'}" Click="HandleStartStopClicked" />
    </StackPanel>
</UserControl>

Au travers de différentes techniques, un UserControl peuvent être ajoutées dynamiquement à la Window. Peut-être via le Bouton dans la fenêtre. Peut-être, de façon plus problématique, à partir d'un stockage de sauvegarde persistant lorsque l'application est démarrée.

Comme on peut le voir à partir du xaml, j'ai décidé qu'il fait sens pour moi d'essayer d'utiliser les Commandes comme un moyen de gérer les différentes opérations que l'utilisateur peut effectuer avec Tasks. Je fais cela avec l'objectif final de l'affacturage toute logique de commande dans un plus formellement définis Contrôleur de couche, mais je suis en train de revoir une étape à la fois.

Le problème que je rencontre est lié à l'interaction entre la commande dans le UserControl's ContextMenu et la commande du CanExecute, défini dans la Fenêtre. Lorsque l'application démarre pour la première fois et les Tâches enregistrées sont restaurés dans TaskStopwatches sur la Fenêtre, qu'aucun des éléments de l'INTERFACE sont sélectionnés. Si je puis immédiatement r-cliquez sur un UserControl dans le Window dans une tentative d'exécution de la ViewTaskProperties de commande, le CanExecute gestionnaire ne s'exécute et l'élément de menu reste désactivé. Si je puis cliquez sur certains élément de l'INTERFACE utilisateur (par exemple, le bouton) juste pour donner quelque chose de l'accent, la CanExecute les gestionnaires sont à la CanExecuteRoutedEventArgs's de la Source de jeu de propriétés de l'élément d'INTERFACE utilisateur qui a le focus.

Dans une certaine mesure, ce problème semble être connu-j'ai appris que les menus de l'itinéraire de la manifestation par le dernier élément qui a le focus pour éviter de toujours envoyer l'événement à partir de l'élément de menu. Ce que je voudrais, cependant, est la source de l'événement le contrôle de lui-même, ou de la Tâche que le contrôle est d'habillage sur elle-même (mais Task n'est pas un Élément, donc je ne pense pas qu'elle peut être une source).

J'ai pensé que peut-être j'ai été absent de la CommandTarget bien sur la MenuItem dans le UserControl, et ma première pensée a été que j'ai voulu la commande provenant de l'objet UserControl, c'est donc naturellement que j'ai d'abord essayé:

<MenuItem x:Name="mnuProperties" 
          Header="_Properties" 
          Command="{x:Static localcommands:TaskCommands.ViewTaskProperties}" 
          CommandTarget="{Binding ElementName=This}" />

Cet échec comme une défaillance de la liaison. Je ne sais pas pourquoi. Puis j'ai pensé, "Hmmm, je suis à la recherche de l'arbre, alors peut-être ce dont j'ai besoin est un RelativeSource" et j'ai essayé ceci:

<MenuItem x:Name="mnuProperties" 
          Header="_Properties" 
          Command="{x:Static localcommands:TaskCommands.ViewTaskProperties}" 
          CommandTarget="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:TaskStopwatchControl}}}" />

Qui a également échoué, mais quand j'ai regardé mon code xaml de nouveau, j'ai réalisé que la ContextMenu est dans une propriété de l'objet UserControl, ce n'est pas un élément enfant. J'ai donc deviné (et à ce stade, c'est une supposition):

<MenuItem x:Name="mnuProperties" 
          Header="_Properties" 
          Command="{x:Static localcommands:TaskCommands.ViewTaskProperties}" 
          CommandTarget="{Binding RelativeSource={x:Static RelativeSource.Self}}" />

Et qui a aussi échoué.

Un échec de deviner et vérifier comme c'est assez pour me faire marche arrière et de réaliser que je suis absent une sorte de concept fondamental ici, cependant. Alors, que dois-je faire?

  1. Est ma compréhension re: le rôle de CommandTarget correct en ce que cela permet de modifier le code source d'une commande?
  2. Comment puis-je lier à partir d'un MenuItem dans UserControl.ContextMenu à la possession d' UserControl? Ou suis-je en train de faire quelque chose de mal, tout simplement parce que je perçois un besoin?
  3. Est mon désir d'avoir le contexte d'une commande définie par l'élément qui a été cliqué pour générer le menu contextuel, par opposition à l'élément qui a le focus avant le menu contextuel, incorrect? Peut-être que j'ai besoin d'écrire ma propre commande au lieu de l'aide de l' RoutedUICommand:
    private static RoutedUICommand viewTaskPropertiesCommand = new RoutedUICommand("View a task's details.", "ViewTaskProperties", typeof(TaskCommands));
    public static RoutedUICommand ViewTaskProperties
    {
        get { return viewTaskPropertiesCommand; }
    }
  4. Est-il une profonde faille fondamentale dans mon design? C'est mon premier gros projet WPF, et je suis en train de faire sur mon propre temps comme une expérience d'apprentissage, donc je ne suis certainement pas opposé à l'apprentissage d'une solution supérieure d'architecture.

OriginalL'auteur Greg D | 2009-03-05