关于wpf:命令绑定到ContextMenu(在ListBox中的ListBoxItem上)不起作用

Command bind to ContextMenu (which on ListBoxItem in ListBox) don't work

本问题已经有最佳答案,请猛点这里访问。

在WPF中,使用MVVM指示灯,有一个Class(由一些学生组成),而Class包含一些Student

enter image description here

右键单击一个学生的名字,然后显示MessageBox,这样就可以了:

ClassDetailView.xaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<UserControl DataContext="{Binding ClassDetail, Source={StaticResource Locator}}">
    <DockPanel>
        <ListBox
            ItemsSource="{Binding Students}"
            DisplayMemberPath="Name">
            <ListBox.ContextMenu>
                <ContextMenu DataContext="{Binding Path=PlacementTarget, RelativeSource={RelativeSource Self}}">
                    <MenuItem
                        Header="Show Selected"
                        Command="{Binding Path=DataContext.RemoveStudentCommand}"
                        CommandParameter="{Binding Path=SelectedItem}"/>
                </ContextMenu>
            </ListBox.ContextMenu>
        </ListBox>
    </DockPanel>
</UserControl>

但是,它不能以这种方式工作(使用ListBox.ItemContainerStyle):

1
2
3
4
5
6
7
8
9
10
11
12
13
<ListBox.ItemContainerStyle>
    <Style TargetType="ListBoxItem">
        <Setter Property="ContextMenu">
            <Setter.Value>
                <ContextMenu DataContext="{Binding Path=PlacementTarget, RelativeSource={RelativeSource Self}}">
                    <MenuItem Header="Show Selected"
                            Command="{Binding Path=DataContext.RemoveStudentCommand}"
                            CommandParameter="{Binding Path=SelectedItem}"/>
                 </ContextMenu>
             </Setter.Value>
         </Setter>
     </Style>
 </ListBox.ItemContainerStyle>

代替

1
2
3
4
5
<ListBox.ContextMenu>
    <ContextMenu ...>
        ...
    <ContextMenu />
</ListBox.ContextMenu>

ClassDetailViewModel.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
namespace ContextMenu.ViewModel
{
    public class ClassDetailViewModel : ViewModelBase
    {
        public ClassDetailViewModel()
        {
            CreateData();
        }

        public void CreateData()
        {
            students.Add(new StudentViewModel() { Name ="QQ" });
            students.Add(new StudentViewModel() { Name ="WW" });
            students.Add(new StudentViewModel() { Name ="EE" });
            students.Add(new StudentViewModel() { Name ="RR" });
            students.Add(new StudentViewModel() { Name ="AA" });
            students.Add(new StudentViewModel() { Name ="SS" });
            students.Add(new StudentViewModel() { Name ="DD" });
            students.Add(new StudentViewModel() { Name ="FF" });
            students.Add(new StudentViewModel() { Name ="ZZ" });
            students.Add(new StudentViewModel() { Name ="XX" });
        }

        public const string StudentsPropertyName ="Students";
        private ObservableCollection<StudentViewModel> students =
            new ObservableCollection<StudentViewModel>();
        public ObservableCollection<StudentViewModel> Students
        {
            get { return students; }
            set
            {
                if (students == value) { return; }
                students = value;
                RaisePropertyChanged(StudentsPropertyName);
            }
        }

        private RelayCommand<StudentViewModel> removeStudentCommand;
        public RelayCommand<StudentViewModel> RemoveStudentCommand
        {
            get
            {
                return removeStudentCommand
                    ?? (removeStudentCommand =
                        new RelayCommand<StudentViewModel>(ExecuteRemoveStudentCommand));
            }
        }
        private void ExecuteRemoveStudentCommand(StudentViewModel student)
        {
            if (null == student) { return; }
            MessageBox.Show(string.Format("RemoveStudent:{0}", student.Name));
        }
    }
}

StudentViewModel.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
namespace ContextMenu.ViewModel
{
    public class StudentViewModel : ViewModelBase
    {
        public const string NamePropertyName ="Name";
        private string name ="";
        public string Name
        {
            get { return name; }
            set
            {
                if (name == value) { return; }
                name = value;
                RaisePropertyChanged(NamePropertyName);
            }
        }
    }
}

您需要一个代理将命令绑定到listboxitem的上下文菜单。 看到答案:

[WPF] How to bind to data when the DataContext is not inherited


你可以使用contextmenu作为资源吗?

就像是:

1
2
3
4
5
6
7
8
<UserControl.Resources>

<ContextMenu x:name="contextMenuExample" DataContext="{Binding Path=PlacementTarget, RelativeSource={RelativeSource Self}}">
                    <MenuItem Header="Show Selected"
                            Command="{Binding Path=DataContext.RemoveStudentCommand}"
                            CommandParameter="{Binding Path=SelectedItem}"/>
                 </ContextMenu>
</UserControl.Resources>

然后在列表上,执行以下操作:

1
<Listbox ContextMenu = {StaticResource contextMenuExample} ... />

或者你真的想使用ItemContainerStyle吗?

from - >如何右键单击列表框中的项目并打开WPF上的菜单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<ListBox Name="someListBox" MouseDown="someListBox_MouseDown">
    <ListBox.Resources>

        <!--Defines a context menu-->
        <ContextMenu x:Key="MyElementMenu">
            <MenuItem Header="Delete" Click="MenuItemDelete_Click"/>
        </ContextMenu>

        <!--Sets a context menu for each ListBoxItem in the current ListBox-->
        <Style TargetType="{x:Type ListBoxItem}">
             <Setter Property="ContextMenu" Value="{StaticResource MyElementMenu}"/>
        </Style>

    </ListBox.Resources>
    <ListBoxItem>...</ListBoxItem>
    <ListBoxItem>...</ListBoxItem>
    <ListBoxItem>...</ListBoxItem>
</ListBox>


通过将ContextMenu移动到ListBoxItem,您已将DataContext从ClassDetailsViewModel(ListBox的DataContext)更改为StudentViewModel(ListBoxItem的DataContext)。 因此,您需要更改路径以访问父ListBox的DataContext以访问RelayCommand。

1
2
3
4
5
6
7
8
9
10
11
12
13
<ListBox.ItemContainerStyle>
    <Style TargetType="ListBoxItem">
        <Setter Property="ContextMenu">
            <Setter.Value>
                <ContextMenu DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}}, Path=DataContext}">
                    <MenuItem Header="Show Selected"
                            Command="{Binding Path=RemoveStudentCommand}"
                            CommandParameter="{Binding Path=SelectedItem}"/>
                 </ContextMenu>
             </Setter.Value>
         </Setter>
     </Style>
 </ListBox.ItemContainerStyle>