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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19447795/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-13 09:53:40  来源:igfitidea点击:

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

wpfmvvmcommandcontextmenu

提问by SubmarineX

In WPF, with MVVM light, there's a Class(which is consist of some students), and the Classhold some Students.

在 WPF 中,使用 MVVM 灯,有一个Class(由一些学生组成)和Class一些Students。

enter image description here

在此处输入图片说明

Right-Click one Student's name, then will show a MessageBox, it is ok in this way:

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

ClassDetailView.xaml

ClassDetailView.xaml

<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>

But, it don't work in this way (use ListBox.ItemContainerStyle):

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

<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>

instead of

代替

<ListBox.ContextMenu>
    <ContextMenu ...>
        ...
    <ContextMenu />
</ListBox.ContextMenu>

ClassDetailViewModel.cs

ClassDetailViewModel.cs

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

学生视图模型.cs

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);
            }
        }
    }
}

回答by James

You need a proxy to bind commands to a context menu of a listboxitem. See the answer here:

您需要一个代理来将命令绑定到列表框项的上下文菜单。请看这里的答案:

http://www.thomaslevesque.com/2011/03/21/wpf-how-to-bind-to-data-when-the-datacontext-is-not-inherited/

http://www.thomaslevesque.com/2011/03/21/wpf-how-to-bind-to-data-when-the-datacontext-is-not-inherited/

回答by sexta13

Can you use the contextmenu as a resource?

您可以将上下文菜单用作资源吗?

Something like:

就像是:

<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>

Then on list, do something like:

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

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

Or do you really want to use ItemContainerStyle?

或者你真的想使用 ItemContainerStyle 吗?

from -> how to right click on item from Listbox and open menu on WPF

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

<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>

回答by Mark Green

By moving the ContextMenu to the ListBoxItem, you've changed the DataContext from ClassDetailsViewModel(the DataContext of the ListBox) to StudentViewModel (the DataContext of the ListBoxItem). As a result, you need to change your path to access the parent ListBox's DataContext to get access to the RelayCommand.

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

<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>