Command bind to ContextMenu (which on ListBoxItem in ListBox) don't work
Solution 1
You need a proxy to bind commands to a context menu of a listboxitem. See the answer here:
Solution 2
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?
from -> how to right click on item from Listbox and open menu on 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>
Solution 3
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.
<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>
Related videos on Youtube
SubmarineX
Updated on June 04, 2022Comments
-
SubmarineX almost 2 years
In WPF, with MVVM light, there's a
Class
(which is consist of some students), and theClass
hold someStudent
s.Right-Click one Student's name, then will show a
MessageBox
, it is ok in this way: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> <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
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
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); } } } }
-
SubmarineX over 10 yearsI just want to show the ContextMenu only when right-click the ListBoxItem rather than entire ListBox.
-
SubmarineX over 10 yearsIn this way, it also don't work.
-
sexta13 over 10 yearstake a look at:stackoverflow.com/questions/9549231/…
-
SubmarineX over 10 yearsI don't think they are different if use ListBox.Resources. And use MVVM in my app, so it will be more complex.
-
sexta13 over 10 yearsyou can have the ContextMenu in resources in the user control, and then apply it in different listboxes with the same behaviour. The only thing you will have to do in those listboxes is the style part...
-
SubmarineX over 10 yearscould you transform above to mvvm? After all, i think that is a problem with the DataContent.
-
Vasudevan Kannan over 8 yearsThis solution worked fine for me ,also I had read earlier about a similar solution by Josh Smith
-
satnhak over 8 yearsYes! You can also bind to a control using this technique with a
RelativeSource
binding. Thanks. -
blandau over 8 yearsThis was the closest answer that worked for me. The only difference is I needed to consume the resource as a DynamicResource because my Command binding was relative to the UserControl's DataContext. Here's an answer to the difference between Static and Dynamic resource stackoverflow.com/a/200875/403725
-
Taras over 6 yearsIt actualy works, but for some wierd reason only if ContextMenu defined as resouce.