Click event on a (Context)MenuItem of a ListBox (XamlParseException) - c#

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>

Related

WPF Context Menu Click event works in one place, not in another

I have the following XAML that produces a ListBox where each item contains another ListBox inside an Expander, and I have defined PageContextMenu as the context menu for the top level list and FrameContextMenu for the lower level list.
The problem: Both are displayed correctly, but click events are only working on the top level context menu and not the lower level context menu. For example, clicking on Delete Selected in PageContextMenu correctly invokes the associated handler, but clicking on Delete Selected Frame(s) in FrameContextMenu does NOT fire the associated handler. I'm not seeing any indication of an error, and even if I put a breakpoint in ContextDeleteFrames_Click it doesn't get hit. It's as if there's no handler associated with that menu entry at all.
I've looked at a number of other questions relating to context menus not working, but none seemed applicable. Is there some problem with the two list boxes being nested?
XAML:
<ListBox Name="PageListBox" ItemsSource="{Binding CurrentPack.Pages}" HorizontalAlignment="Stretch" SelectionMode="Extended">
<ListBox.Resources>
<ContextMenu x:Key="PageContextMenu">
<MenuItem Header="_Add" Name="ContextAddAddPage"/>
<MenuItem Header="_Edit" Name="ContextEditPage"/>
<MenuItem Header="_Delete Selected" Name="ContextDeletePage" Click="ContextDeletePage_Click"/>
</ContextMenu>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="ContextMenu" Value="{StaticResource PageContextMenu}"/>
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"/>
<Setter Property="HorizontalAlignment" Value="Stretch"/>
</Style>
</ListBox.Resources>
<ListBox.ItemTemplate>
<DataTemplate >
<Border BorderThickness="2" BorderBrush="White" HorizontalAlignment="Stretch">
<StackPanel HorizontalAlignment="Stretch">
<Label Content="{Binding PresentationName}"/>
<Expander VerticalAlignment="Top" HorizontalAlignment="Stretch">
<Expander.Header>
<Label Content="{Binding FrameStatusText}"/>
</Expander.Header>
<ListBox Name="FrameListBox" ItemsSource="{Binding Frames}" HorizontalAlignment="Stretch" SelectionMode="Extended">
<ListBox.Resources>
<ContextMenu x:Key="FrameContextMenu">
<MenuItem Header="_Add Frame" Name="ContextAddFrame"/>
<MenuItem Header="_Edit Frame" Name="ContextEditFrame"/>
<MenuItem Header="_Delete Selected Frame(s)" Name="ContextDeleteFrames" Click="ContextDeleteFrames_Click"/>
<MenuItem Header="Show _Preview" Name="ContextShowPreview" Click="ContextShowPreview_Click"/>
</ContextMenu>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="ContextMenu" Value="{StaticResource FrameContextMenu}"/>
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"/>
</Style>
</ListBox.Resources>
<ListBox.ItemTemplate>
<DataTemplate>
<Label Content="{Binding PresentationName}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Expander>
</StackPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
Code Behind:
private void ContextDeletePage_Click(object sender, RoutedEventArgs e)
{ //this works
Workspace.Content.DeleteSelectedPages();
}
private void ContextDeleteFrames_Click(object sender, RoutedEventArgs e)
{ //this doesn't!
Workspace.Content.DeleteSelectedFrames();
}
Don't use events in a DataTemplate. They won't work.
Either move your second context menu from the DataTemplate's resources into the PageListBox resources like this:
<ListBox Name="PageListBox">
<ListBox.Resources>
<!-- ... other resources... -->
<ContextMenu x:Key="FrameContextMenu">
<MenuItem Header="_Add Frame" Name="ContextAddFrame"/>
<MenuItem Header="_Edit Frame" Name="ContextEditFrame"/>
<MenuItem Header="_Delete Selected Frame(s)" Click="ContextDeleteFrames_Click"/>
<MenuItem Header="Show _Preview" Name="ContextShowPreview" Click="ContextShowPreview_Click"/>
</ContextMenu>
</ListBox.Resources>
</ListBox>
...or use commands instead of events:
<MenuItem Header="_Delete Selected Frame(s)" Command="{Binding DeleteFrameCommand}"/>
where DeleteFrameCommand is a property of type ICommand or RoutedCommand.
If you want to use commands, you should be aware that a context menu is not in the visual tree of its PlacementTarget, so you'll have to use some helpers to make the bindings work (a binding proxy or PlacementTarget.Tag property etc.)

Command not getting called when MenuItem is clicked

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

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

MenuItem Vertical alignment issue

I am facing an issue with MenuItem that whenever I specify an access key, the header alignment is getting disrupted. Below is a sample image wherein I have specified "_New" for New menu item and bound it to New command. You can observe tha the Text "New" is align at bottom and shortcut key is aligned at top. Also for other menu items I have not specified any access key, so they have no issue.
Here is the XAML
<StackPanel DockPanel.Dock="Top">
<Menu Padding="0,5">
<MenuItem x:Name="MnuTask" Header="Task">
<MenuItem x:Name="MnuNew" Header="_New" Command="New"/>
<MenuItem x:Name="MnuSave" Header="Save" Command="Save"/>
<MenuItem x:Name="MnuDelete" Header="Delete" Command="Delete"/>
<Separator/>
<MenuItem x:Name="MnuRefresh" Header="Reload Data" Command="{x:Static Local:MainWindow.RefreshDataCommand}" />
<MenuItem x:Name="MnuHistory" Header="View Range History" Command="{x:Static Local:MainWindow.RangeHistoryCommand}" />
<Separator/>
<MenuItem x:Name="MnuExit" Header="Exit" Command="Close"/>
</MenuItem>
<MenuItem x:Name="MnuView" Header="View">
<MenuItem x:Name="MnuFind" Header="Find Formula"/>
</MenuItem>
</Menu>
</StackPanel>
Could any body let me know what's going on?
Found it. I had the below TextBlock Style present in Resources section of my window. Commenting those lines resolved the issue. (But now I need to apply TextBlock style explicitly using keys :( )
<Style TargetType="TextBlock">
<Setter Property="Margin" Value="3,6,3,0"/>
<Setter Property="VerticalAlignment" Value="Bottom"/>
</Style>

Tip / idea how can I get a generic context menu?

I want to develop a generic contextmenu in wpf with mvvm. I would like to display it in each viewModel that have a listview with the style: ItemContainerStyle="{DynamicResource ListViewItemContainerStyle}".
The context menu will have 2 options that will be enabled or not depending on some constraints in each view Model. The enabled or not will be managed by the canexecute of the command.
I have no problem if I put it in each view inside the listview, but I would like to have it only in one place other than in each listview. I have tried to put it in the ListViewItemContainerStyle but it shows an exception "it is not possible to add a System.Windows.Controls.ContextMenu to a System.Object". Here is my code snippet
<Style x:Key="ListViewItemContainerStyle"
TargetType="ListViewItem">
.
.
.
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Command="{Binding Path=Command}"
CommandParameter="AddNew">
<MenuItem.Header>
<TextBlock Text="{x:Static p:TextResources.New}" />
</MenuItem.Header>
</MenuItem>
<MenuItem Command="{Binding Path=Command}"
CommandParameter="Delete">
<MenuItem.Header>
<TextBlock Text="{x:Static p:TextResources.Delete}" />
</MenuItem.Header>
</MenuItem>
</ContextMenu>
</Setter.Value>
</Setter>
.
.
.
Any idea of how to do this?
Try this:
<ContextMenu x:Shared="False" x:Key="ListViewContextMenu>
<MenuItem Command="{Binding Path=Command}"
CommandParameter="AddNew">
<MenuItem.Header>
<TextBlock Text="{x:Static p:TextResources.New}" />
</MenuItem.Header>
</MenuItem>
<MenuItem Command="{Binding Path=Command}"
CommandParameter="Delete">
<MenuItem.Header>
<TextBlock Text="{x:Static p:TextResources.Delete}" />
</MenuItem.Header>
</MenuItem>
</ContextMenu>
<Style x:Key="ListViewItemContainerStyle"
TargetType="ListViewItem">
<Setter Property="ContextMenu" Value="{StaticResource ListViewContextMenu}"/>

Categories

Resources