1
votes

Comment lier le menu contextuel de l'élément d'affichage de liste à ICommand

J'essaye de lier le menu contextuel de l'élément listview à l'ICommand contenu dans ma VM mais je n'arrive pas à le lier.

J'ai essayé le code suivant pour afficher le menu contextuel sur l'élément listview et le lier à ma VM.

Afficher le code

public sealed class StudyVM : BaseVM {
   public RelayCommand LockCommand { get; set; }

   public StudyVM() {
       LockCommand = new RelayCommand(() => ExecuteLockCommad());
   }

   void ExecuteLockCommad() {
       //Some code to be execute when menu item clicked
   }
}

Code de la VM

<ListView ItemsSource="{Binding StudyList, Mode=TwoWay}" SelectedItem="{Binding SelectedStudy, Mode=TwoWay}" >
    <ListView.Resources>
        <ContextMenu x:Key="ItemContextMenu" ItemsSource="{Binding StudyVM}">
            <MenuItem Header="Lock" Command="{Binding LockCommand}"/>
        </ContextMenu>
    </ListView.Resources>

    <ListView.ItemContainerStyle>
        <Style TargetType="{x:Type ListViewItem}">
            <Setter Property="ContextMenu" Value="{StaticResource ItemContextMenu}" />
        </Style>
    </ListView.ItemContainerStyle>

    <ListView.View>
        <GridView>
            <GridViewColumn Header="Status" DisplayMemberBinding="{Binding FullStatus}" Width="60"/>
            <GridViewColumn Header="Patient Name" DisplayMemberBinding="{Binding FullName}" Width="350"/>
        </GridView>
    </ListView.View>
</ListView>

J'ai défini le DataContext de ma vue sur StudyVM. Veuillez noter que j'ai ignoré du code (qui est hors sujet et fonctionne bien) lié à la source de l'élément de vue de liste et à l'élément sélectionné de la vue de liste.

Toutes les parties de la vue fonctionnent bien comme tous les éléments de la vue de liste sont affichés dans la liste, le menu contextuel s'affiche lorsque nous cliquons sur l'élément de la liste. Mais le seul problème est que la méthode ExecuteLockCommad ne s'exécute pas lorsque nous cliquons sur l'élément de menu, même si je le lie à l'élément de menu contextuel via LockCommand.


8 commentaires

Avez-vous vérifié la fenêtre Sortie dans votre Visual Studio? Vérifiez les erreurs de liaison. Aussi pourquoi avez-vous besoin d'un nom pour votre ListView?


Le nom ListView est supprimé car il n'est pas nécessaire.


Avez-vous vérifié la fenêtre Sortie?


Pouvez-vous également partager l'implémentation de RelayCommand ? Et les autres commandes fonctionnent-elles?


La fenêtre de sortie affiche le résultat suivant: Erreur System.Windows.Data: 40: Erreur de chemin BindingExpression: propriété «LockCommand» introuvable sur «objet» «StudyEntity» (HashCode = 1681281346) ». BindingExpression: Path = LockCommand; DataItem = 'StudyEntity' (HashCode = 1681281346); l'élément cible est 'MenuItem' (Name = ''); la propriété cible est 'Command' (type 'ICommand')


La classe RelayCommand fonctionne très bien car j'ai de nombreuses commandes pour d'autres contrôles comme le bouton.


Votre problème se situe dans le contexte des données du menu contextuel. Ce que vous devez faire est de pointer la commande vers le parent comme ceci: Command = "{Binding RelativeSource = {RelativeSource AncestorType = ListView}, Path = DataContext.LockCommand}" . Cela devrait résoudre votre problème.


@XAMlMAX, j'ai défini le DataContext du menu contextuel comme suggéré par vous et cela fonctionne parfaitement pour moi. merci pour la même chose.


3 Réponses :


0
votes

J'ai essayé de modifier et de simplifier davantage votre code ...

MainWindow.xaml

using GalaSoft.MvvmLight.Command;

namespace WpfApp5
{
    public sealed class MainWindowViewModel : ViewModelBase
    {
        public RelayCommand LockCommand { get; set; }

        public MainWindowViewModel()
        {
            LockCommand = new RelayCommand(() => ExecuteLockCommand());
        }
        void ExecuteLockCommand()
        {
            //Some code to be execute when menu item clicked
        }
    }
}

MainWindowViewModel.cs strong>

<Window x:Class="WpfApp5.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp5"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">

    <Window.DataContext>
        <local:MainWindowViewModel/>
    </Window.DataContext>

    <Grid Margin="5,5,5,5">
        <ListView Name="lvSelectStudy"  >
            <!--ItemsSource="{Binding StudyList, Mode=TwoWay}" SelectedItem="{Binding SelectedStudy, Mode=TwoWay}"-->
            <ListView.Resources>
                <ContextMenu x:Key="ItemContextMenu">
                    <!--ItemsSource="{Binding StudyVM}"-->
                    <MenuItem Header="Lock" Command="{Binding LockCommand}"/>
                </ContextMenu>
            </ListView.Resources>

            <ListView.ItemContainerStyle>
                <Style TargetType="{x:Type ListViewItem}">
                    <Setter Property="ContextMenu" Value="{StaticResource ItemContextMenu}" />
                </Style>
            </ListView.ItemContainerStyle>
            <!--<ListView.View>
                <GridView>
                    <GridViewColumn Header="Status" DisplayMemberBinding="{Binding FullStatus}" Width="60"/>
                    <GridViewColumn Header="Patient Name" DisplayMemberBinding="{Binding FullName}" Width="350"/>
                </GridView>
            </ListView.View>-->
            <ListViewItem>Item1</ListViewItem>
            <ListViewItem>Item2</ListViewItem>
            <ListViewItem>Item3</ListViewItem>
            <ListViewItem>Item4</ListViewItem>
            <ListViewItem>Item5</ListViewItem>
        </ListView>
    </Grid>
</Window>

Si vous mettez un point de rupture dans void ExecuteLockCommand () , il est appelé après un clic droit sur un ListViewItem ... Notez que cette méthode, dans votre code, avait un nom différent de celui défini dans le constructeur de votre ViewModel


2 commentaires

À propos du nom différent, c'est une faute de frappe et j'ai modifié ma question en conséquence. Désolé, mais je n'ai pas vu de différence majeure entre mon code et votre suggestion de codage. J'ai changé mon code selon votre suggestion (propriété ItemSource supprimée de ContextMenu) mais ExecuteLockCommand ne s'exécute pas (vérifié via le point d'arrêt.)


Utilisez-vous RelayCommand de GalaSoft? Je demande, car j'ai eu des problèmes avec un RelayCommand "fait maison" ...



2
votes

Le problème que vous avez rencontré avec votre code était le DataContext du ContextMenu .
Votre liaison ConextMenu actuelle résoudrait la commande à partir du contexte actuel. Puisque le ContextMenu est attribué par ListViewItem , c'est DataContext quel que soit le ListViewItem . Dans votre cas, StudyEntity .
Ce n'est cependant pas un problème majeur, la liaison peut être pointée vers un autre contexte. Ce qui dans votre cas doit être StudyVM . Comme ce serait le DataContext de votre ListView , nous pouvons le pointer via cet extrait de code:

<ListView Name="ListView" ItemsSource="{Binding StudyList, Mode=TwoWay}" SelectedItem="{Binding SelectedStudy, Mode=TwoWay}">
    <ListView.Resources>
        <ContextMenu x:Key="ContextMenu">
            <MenuItem Header="Lock" Command="{Binding ElementName=ListView, Path=DataContext.LockCommand}"/>
        </ContextMenu>
    </ListView.Resources>
    ...
</ListView>  

Article de MSDN sur RelativeSource MarkupExtension
Un autre moyen d'obtenir ce DataContext pourrait être fait via Name . (Je sais que je vous ai demandé pourquoi vous l'utilisez et c'est pourquoi)

Command="{Binding RelativeSource={RelativeSource AncestorType=ListView}, Path=DataContext.LockCommand}"  

Cela permet d'obtenir le même résultat mais, il y a un inconvénient à utiliser des noms dans WPF (avec x: nom pour être exact). Article sur les fuites de mémoire .


0 commentaires

0
votes

Le problème est que le ContextMenu n'est pas dans l'arborescence visuelle, donc vous devez essentiellement indiquer au menu contextuel quel contexte de données utiliser. Vous pouvez passer le datacontext via la propriété tag du conteneur comme ci-dessous.

<ListView Name="lvSelectStudy" Tag="{Binding DataContext, RelativeSource={RelativeSource Self}}">
    <ListView.Resources>
        <ContextMenu DataContext="{Binding PlacementTarget.Tag, RelativeSource {RelativeSource Self}}">                                
            <MenuItem Header="Lock" Command="{Binding LockCommand}"/>
        </ContextMenu>
    </ListView.Resources>
    ...
</ListView>


0 commentaires