Command not getting called when MenuItem is clicked - c#

I have a context menu that gets called when a listview is right-clicked. After doing some research I found out that this is how you bind a command to a menuitem.
<ListView.ContextMenu>
<ContextMenu>
<MenuItem Header="Delete">
<MenuItem.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Command" Value="{Binding Path=DataContext.DeleteCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListView}}"/>
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
</ContextMenu>
</ListView.ContextMenu>
Any help on why the command isn't firing would be much appreciated. Thanks.

<MenuItem Header="Delete" Command="{Binding DeleteCommand}"/>
will do it for you.
ItemContainerStyle is used when there is ItemsSource for MenuItem.
CodeProject for Menu+Commands

Related

Get name(or index) of selected menu item from context menu, which was dynamically generated via ItemsSource bound to a ObservableCollection

I have a context menu that contains 1 menu item. That menu item is bound to a ObservableCollection for the itemssource.
<ListView.ContextMenu>
<ContextMenu>
<MenuItem Header="Example Menu Item"
Command="{Binding Path=DataContext.ExampleCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListView}}"
ItemsSource="{Binding ObservableItems}">
</MenuItem>
</ContextMenu>
</ListView.ContextMenu>
How do I get the name (or index) of the menu item that was selected. The problem is I cannot bind a command to each individual menu item, as they are dynamically generated.
For example how would I know which item was clicked, as seen in the image below?
Any help is much appreciated. Thanks.
You still can bind Command and CommandParameter per item for dynamically generated lists but you need to use ItemContainerStyle
<ContextMenu>
<MenuItem Header="Example Menu Item" ItemsSource="{Binding ObservableItems}">
<MenuItem.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Command" Value="{Binding Path=DataContext.ExampleCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListView}}"/>
<Setter Property="CommandParameter" Value="{Binding}"/>
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
</ContextMenu>
in this example CommandParameter, which is passed to you ExampleCommand command as parameter, will be an item in your collection (current DataContext of child item)
EDIT
To get index you can use pair of ItemsControl properties: AlternationCount and AlternationIndex. You set AlternationCount to number of items in your collection and pass AlternationIndex to your command
<MenuItem Header="Example Menu Item" ItemsSource="{Binding ObservableItems}" AlternationCount="{Binding ObservableItems.Count}">
<MenuItem.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Command" Value="{Binding ...}"/>
<Setter Property="CommandParameter" Value="{Binding RelativeSource={RelativeSource Self}, Path=(ItemsControl.AlternationIndex)}"/>
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>

WPF MenuItem not filling available ContextMenu space

I'm having a weird problem with a simple ContextMenu using MahApps.Metro without any additional styling. When moving the cursor on top of the text or slightly around it, there is no problem. But when moving it further away, still inside the ContextMenu bounds, the Cursor is no longer on top of the MenuItem. Clicking now also doesn't result in any action at all besides closing the ContextMenu.
<ContextMenu ItemsSource="{Binding ContextItems}">
<ContextMenu.ItemTemplate>
<DataTemplate>
<MenuItem Header="{Binding Text}" Command="{Binding Command}"/>
</DataTemplate>
</ContextMenu.ItemTemplate>
</ContextMenu>
What am I doing wrong? Why doesn't the MenuItem use the available space?
If your ContextItems holds a collection with viewmodels then I think this could help you (not tested):
<ContextMenu ItemsSource="{Binding ContextItems}">
<ContextMenu.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Header" Value="{Binding Text}" />
<Setter Property="Command" Value="{Binding Command}" />
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
Command and Text should be the properties on the viewmodel object.
I havent used MahApps.Metro . Though you can override the template like this
<ContextMenu.ItemTemplate>
<DataTemplate>
<MenuItem Header="{Binding Text}" Command="{Binding Command}"/>
<MenuItem.Template>
<ControlTemplate>
<ContentPresenter Content="{Binding Header,RelativeSource={RelativeSource TemplatedParent}}">
</ContentPresenter>
</ControlTemplate>
</MenuItem.Template>
</MenuItem>
</DataTemplate>
</ContextMenu.ItemTemplate>
I hope this will help.

Adding commands for Contextmenu inside ListViewItem Style

I am searching for a solution for the ContextMenu's commands defined under ListViewItem style. Binding was successful when I defined and bind the commands inside ContextMenu within ListView.
I used viewModel class file to define the commands ex: ExecuteClone, CanExecuteClone which i am trying to bind it with ContextMenu menu items using Command Binding.
<ListView.ContextMenu>
<ContextMenu >
<MenuItem Header="New" Command="{Binding AddCommand}" />
</ContextMenu>
</ListView.ContextMenu>
But when i change the ContextMenu from ListView to ListViewItem, it doesn't hit the ViewModel command.
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Header="New" Command="{Binding AddCommand}"/>
<MenuItem Header="Clone" Command="{Binding CloneCommand}"/>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
It needs to hit the ViewModel AddCommand, CloneCommand which are DelegateCommand actions defined under ViewModel class.
I found the answer with some trials, anyways thanks for the help.
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="Tag" Value="{Binding RelativeSource={RelativeSource AncestorType=ListView}, Path=DataContext}"/>
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu DataContext="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
<MenuItem Header="New" Command="{Binding AddCommand}"/>
<MenuItem Header="Clone" Command="{Binding CloneCommand}"/>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
The ListViewItems have a different DataContext than the ListView. Every ListViewItem has its DataContext set to one of the items in your ItemsSource collection, so this is why the binding doesn't work. If you want to bind the commands to the ListView's DataContext, you can do it like that:
Command="{Binding DataContext.AddCommand, RelativeSource={RelativeSource FindAncestor, AncestorType=ListView}}"

Binding a DataGridCell ContextMenu MenuItem to a Command on a ViewModel

I know that there are similar questions on SO.
I have reviewed them, tried them, tried combinations of them, and retried them for the past day an half with very little success. I'm apparently dense or missing something.
The basic approaches covered were:
Placement Target
Using the Tag Attribute on a Parent
Using Ancestor Relative Source
Making use of ElementName root for the DataContext
Nothing seems to work and in only one instance am I even able to hit a break point in a Test Converter to see whats being bound. It will only work in the Tag Attribute of the DataCell. I am exasperated. This is as close as I have gotten:
<xcdg:DataGridControl.Resources>
<Style TargetType="{x:Type xcdg:DataCell}">
<Setter Property="Tag" Value="{Binding DataContext, RelativeSource={RelativeSource AncestorType={ x:Type v:RTCA}}, Converter={StaticResource TestConverter}}" />
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu DataContext="{Binding RelativeSource={RelativeSource AncestorType={ x:Type xcdg:DataCell}}, Path=Tag, Converter={StaticResource TestConverter}}" StaysOpen="True">
<MenuItem Header="Acknowledge" Command="{Binding Path=AcknowledgeViolationCommand}" />
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</xcdg:DataGridControl.Resources>
As a variation on this approach I thought that sense the Binding on the Tag Attribute worked and was correct that I could use in the DataContext for the ContextMenu or the even just the MenuItem itself.
<ContextMenu DataContext="{Binding DataContext, RelativeSource={RelativeSource AncestorType={ x:Type v:RTCA}}, Converter={StaticResource TestConverter}}" StaysOpen="True">
or
<MenuItem Header="Acknowledge" DataContext="{Binding DataContext, RelativeSource={RelativeSource AncestorType={ x:Type v:RTCA}}, Converter={StaticResource TestConverter}}" Command="{Binding Path=AcknowledgeViolationCommand}" />
With this next one I also tried PlacementTarget.DataContext and PlacementTarget.Tag with no results.
<Setter Property="Tag" Value="{Binding DataContext, RelativeSource={RelativeSource AncestorType={ x:Type v:RTCA}}, Converter={StaticResource TestConverter}}" />
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu DataContext="{Binding PlacementTarget, Converter={StaticResource TestConverter}}" StaysOpen="True">
<MenuItem Header="Acknowledge" Command="{Binding Path=AcknowledgeViolationCommand}" />
</ContextMenu>
</Setter.Value>
</Setter>
Other attempts:
<Setter Property="Tag" Value="{Binding DataContext, RelativeSource={RelativeSource AncestorType={ x:Type v:RTCA}}, Converter={StaticResource TestConverter}}" />
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu DataContext="{Binding PlacementTarget, Converter={StaticResource TestConverter}}" StaysOpen="True">
<MenuItem DataContext="{Binding RelativeSource={RelativeSource AncestorType=ContextMenu}, Path=DataContext}" Header="Acknowledge" Command="{Binding Path=AcknowledgeViolationCommand}" />
</ContextMenu>
</Setter.Value>
</Setter>
Here I tried making use of the ElementName root approach in different ways but it would only work in the Tag for the Cell and not the DataContext or Tag for the ContextMenu
<Setter Property="Tag" Value="{Binding DataContext, ElementName=root}" />
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu DataContext="{Binding PlacementTarget, Converter={StaticResource TestConverter}}" StaysOpen="True">
<MenuItem Header="Acknowledge" Command="{Binding Path=PacementTarget.Tag.AcknowledgeViolationCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}, Converter={StaticResource TestConverter}}" />
</ContextMenu>
</Setter.Value>
</Setter>
<!-- variation of above -->
<Setter Property="Tag" Value="{Binding DataContext, ElementName=root}" />
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu DataContext="{Binding RelativeSource={RelativeSource Mode=Self}, Path=PlacementTarget.DataContext, Converter={StaticResource TestConverter}}" StaysOpen="True">
<MenuItem Header="Acknowledge" Command="{Binding Path=AcknowledgeViolationCommand}" />
</ContextMenu>
</Setter.Value>
</Setter>
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu DataContext="{Binding Path=PlacementTarget, RelativeSource={RelativeSource Mode=Self}, Converter={StaticResource TestConverter}}" StaysOpen="True">
<MenuItem Header="Acknowledge" Command="{Binding Path=AcknowledgeViolationCommand}" />
</ContextMenu>
</Setter.Value>
</Setter>
I can list countless failed attempts. PlacementTargets, Tags, Ancestors, RelativeSources, why can't I make this work?. And why can I only seem to step into my TestConverter when its used on the DataCell Tag Binding and no where else?
Heres the Convert Method:
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value;
}
When it fires for Tag binding I can actually see the ViewModel being passed just like I want and I can even execute my Command from the immediate window. Frankly I'm at my wits end on what should have been a 15 minute thing.
Can anyone tell me what I'm doing or wrong?
Additional Example(s)
From Lee O.s Comment I tried the BindingProxy approach without any luck.
<xcdg:DataGridControl.Resources>
<vl:BindingProxy x:Key="proxy" Data="{Binding }" />
<Style TargetType="{x:Type xcdg:DataCell}">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu DataContext="{StaticResource proxy}" StaysOpen="True">
<MenuItem Header="Acknowledge" Command="{Binding Path=AcknowledgeViolationCommand}" />
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
Additional tries. These next attempts did do something new. Now at least when the app is started the breakpoint for the AcknowledgeViolationCommand fires once. However selecting the Item from the Context Menu still does nothing.
<ContextMenu DataContext="{Binding Data, Source={StaticResource proxy}}" StaysOpen="True">
<MenuItem Header="Acknowledge" Command="{Binding Path=AcknowledgeViolationCommand}" />
</ContextMenu>
and
<ContextMenu StaysOpen="True">
<MenuItem Header="Acknowledge" Command="{Binding Path=Data.AcknowledgeViolationCommand, Source={StaticResource proxy}}" />
</ContextMenu>
also tried x:Reference with the following:
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu StaysOpen="True">
<MenuItem Header="Acknowledge" Command="{Binding Path=DataContext.AcknowledgeViolationCommand, Source={x:Reference dummyControl}, Converter={StaticResource TestConverter}}" />
</ContextMenu>
</Setter.Value>
</Setter>
These approaches all have one thing in common in that they all seem to cause a breakpoint I have set in the command to hit ONCE as the grid loads. Further more adding the TestConverter to the reference and setting a breakpoint causes it to hit once for each row in the grid as it loads, but never when the command is clicked.
Here is the code for the Command in the ViewModel
public ICommand AcknowledgeViolationCommand
{
get
{
if (acknowledgeViolationCommand == null) // BreakPoint is here
acknowledgeViolationCommand = new RelayCommand(param => Test());
return acknowledgeViolationCommand;
}
}
private void Test()
{
string a = "a"; //Breakpoint here
}
Made a change to the command in case for some reason it was wanting the canExecute predicate. I confirmed the breakpoint for the canExecute method does fire.
public ICommand AcknowledgeViolationCommand
{
get
{
if (acknowledgeViolationCommand == null) // BP here
acknowledgeViolationCommand = new RelayCommand(param => Test(), param => YesDoIt());
return acknowledgeViolationCommand;
}
}
private bool YesDoIt()
{
return true; //BP here
}

Click event on a (Context)MenuItem of a ListBox (XamlParseException)

I have a ListBox and within it multiple ListBoxItem objects. When the user right-clicks on a ListBoxItem a ContextMenu should appear with some MenuItem objects. The problem that I have is that when I put a Click event on the MenuItem objects I get a XamlParseException stating the following:
A first chance exception of type 'System.Windows.Markup.XamlParseException' occurred in PresentationFramework.dll
Additional information: 'Set connectionId threw an exception.' Line number '31' and line >position '34'.
I have to admit that I don't fully understand styles and resources and the other aspects of WPF. When I was designing this I just copied my code from the Internet. The code is as follows:
<ListBox Grid.Column="1" Grid.Row="1" MouseDoubleClick="MainListBox_MouseDoubleClick" Name="mainListBox" SelectionChanged="MainListBox_SelectionChanged">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Click="OpenMenuItem_Click" Header="Open"/>
<Separator/>
<MenuItem Header="Cut"/>
<MenuItem Header="Copy"/>
<Separator/>
<MenuItem Header="Delete"/>
<MenuItem Header="Rename"/>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
It works when I remove the Click event but of course the ContextMenu becomes useless.
Assign ContextMenu for ListBoxItem as given below.
<ListBox Grid.Column="1" Grid.Row="1" MouseDoubleClick="MainListBox_MouseDoubleClick" Name="mainListBox" SelectionChanged="MainListBox_SelectionChanged">
<ListBox.Resources>
<ContextMenu x:Key="CMenu">
<MenuItem Click="OpenMenuItem_Click" Header="Open"/>
<Separator/>
<MenuItem Header="Cut"/>
<MenuItem Header="Copy"/>
<Separator/>
<MenuItem Header="Delete"/>
<MenuItem Header="Rename"/>
</ContextMenu>
</ListBox.Resources>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="ContextMenu" Value="{StaticResource CMenu}"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBoxItem Content="Test1"/>
</ListBox>

Categories

Resources