I have a listview, that contains multiple child items. I wana to display ContextMenu on rightclick item in that listview.
Actualy i have two menuItems that can call command on certain items, but also i wana to call commands from viewModel.
In example below is my definition of context menu. First two MenuItems call commands directly on ListViewItem and on third im trying to call command from ViewModel assigned to DataContext of user control.
<ListView x:Name="snapshotList" ItemsSource="{Binding SnapshotList}" SelectedItem="{Binding ItemSelected}" Margin="10,81,10,27" MinHeight="100">
<ListView.ItemContainerStyle >
<Style TargetType="ListViewItem">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Header="Copy path" Command="{Binding CopyPathCommand}"/>
<MenuItem Header="Copy path (cmd)" Command="{Binding CopyPathCommandToCmdCommand}"/>
<MenuItem Header="Test" Command="{Binding Path=PlacementTarget.DataContext, RelativeSource={RelativeSource self}}"/>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
I found many similar questions where people claimed that ContextMenu isnt in generalTree co it cant access viewModel/DataContext directly and is necessary to pass it via Tag/Placement target. But when i tried that i always get error in output. So how can i pass dataContext/ViewModel of user control to ContextMenu -> menuItem?
**System.Windows.Data Error: 40 : BindingExpression path error: 'PlacementTarget' property not found on 'object' ''MenuItem' (Name='')'. BindingExpression:Path=PlacementTarget.DataContext; DataItem='MenuItem' (Name=''); target element is 'MenuItem' (Name=''); target property is 'Command' (type 'ICommand')**
Edit3:
Okay so i think i found solution. I had a mistake in traversing xmlTree and if i followed #clemens hints i found this
<ListView x:Name="snapshotList" ItemsSource="{Binding SnapshotList}" SelectedItem="{Binding ItemSelected}" Margin="10,81,10,27" MinHeight="100" >
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem" >
<Setter Property="Tag" Value="{Binding DataContext, ElementName=userControl}"/>
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Header="Copy path" Command="{Binding CopyPathCommand}"/>
<MenuItem Header="Copy path (cmd)" Command="{Binding CopyPathCommandToCmdCommand}"/>
<MenuItem Header="Test" Command="{Binding PlacementTarget.Tag.TestCommand, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}"/>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
Which is working and im understading it in this way:
- from MenuItem i cannot access ListView as PlacementTarget and placement target returns ListViewItem
- So i binded DataContext of UserControl to tag of ListView item
- then in MenuItem i get relativeAncestor ContextMenu (only one i can access) and then took his PlacementTarget (which is ListViewItem) and get his tag and call command on it
Related
I am currently having a problem with my project where I pass the binding of the main viewmodel to the contextmenu.
opening the context menu (using the right mouse button obviously) for the first time gives me this error
System.Windows.Data Error: 40 : BindingExpression path error: '(attached:DependencyObjectAttached.DataContextEx)' property not found on 'object' ''TextBlock' (Name='')'. BindingExpression:Path=PlacementTarget.(attached:DependencyObjectAttached.DataContextEx).QuotationCommandProcessor.ConvertProductCommand; DataItem='ContextMenu' (Name=''); target element is 'MenuItem' (Name=''); target property is 'Command' (type 'ICommand')
It may not be super big of a deal since the binding succeeds the second time the context menu opens, but as OCD as I was, I wish to fix this issue.
So here's what I have
I have a Page, inside the page is a Datagrid, the Datagrid has a Column whose cell template is the PlacementTarget of the ContextMenu
The context menu Command binds to a command of the view model of the page
The implementation i used was through attached property like this
<DataGrid ItemSource="{Binding MyItemSources}">
<DataGrid.Columns>
<DataGridTemplateColumn CellTemplate="{StaticResource MyCellStyle}"/>
<DataGrid.Columns>
</DataGrid>
The style looks like this
<DataTemplate x:Key="MyCellStyle">
<TextBlock Text="{Binding}" attached:DependencyObjectAttached.DataContextEx="{Binding DataContext, RelativeSource={RelativeSource AncestorType={x:Type local:MyPage}}}">
<TextBlock.ContextMenu>
<ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
<MenuItem Header="Click Here To Run Command" Command="{Binding PlacementTarget.(attached:DependencyObjectAttached.DataContextEx).CommandFromTheViewModel, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}" CommandParameter="{Binding}"/>
</ContextMenu>
<TextBlock.ContextMenu>
</TextBlock>
</DataTemplate>
the DependencyObjectAttached.DataContextEx
attached:DependencyObjectAttached.DataContextEx
is an attached property used to pass the viewmodel to the contextmenu
I have already tried using the Tag of the placementtarget (the Textblock) and it is working fine, however, I am using the Tag for some other purpose so attached property was the only option i can think of. Any suggestion?
Please try below code, this achieves the datacontext access of your main window or Page in your case.
Trick is not to create a DataTemplate and instead create ContextMenu as resource directly and then use this context menu for your DataGridCell as shown below.
<Window.Resources>
<ContextMenu x:Key="ContextMenu1">
<ContextMenu.Items>
<MenuItem Header="{Binding DataContext.Title,RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window} }"/>
<MenuItem Header="{Binding DataContext.Title,RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}" />
<MenuItem Header="{Binding DataContext.Title,RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}" />
</ContextMenu.Items>
</ContextMenu>
</Window.Resources>
<Grid>
<DataGrid ItemsSource="{Binding VentingTypesCollection}">
<DataGrid.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="ContextMenu" Value="{StaticResource ContextMenu1}" />
</Style>
</DataGrid.CellStyle>
</DataGrid>
</Grid>
Please remember, DataContext.Title property is a simple string property from my viewmodel.
I think once you achieve access to datacontext, binding anything from viewmodel will be very straight forward.
Hope this helps you.
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>
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}}"
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>
I have a ListView with an ItemSource from a List of string.
Now I have added a context menu which should just implement some commands. But the problem ist how to set the DataContext. Found some solutions but none of these worked for me. Dont know where my fault ist.
Here my XAML code, reduced to the important regions.
<ListView x:Name="lstBackups" ItemsSource="{Binding SelectedClient.Backups}">
<ListView.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Header="Do Something" DataContext="{Binding RelativeSource={RelativeSource AncestorType=ContextMenu}}"
cinch:SingleEventCommand.RoutedEventName="MouseLeftButtonUp"
cinch:SingleEventCommand.TheCommandToRun="{Binding Path=DataContext.OpenBackupInExplorerCommand, ElementName=UserControl}">
</MenuItem>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
</ListView>
I'm sure you've noticed that the ContextMenu is not part of the same visual tree as your user control (annoying I know). So you have to get a bit creative with your binding logic.
Try changing the command binding to the following
cinch:SingleEventCommand.TheCommandToRun="{BindingPath=PlacementTarget.DataContext.OpenBackupInExplorerCommand,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ContextMenu}}}"
That should attempt to get the DataContext of the placement target (in your case the ListView) which should inherit the DataContext of the UserControl
Hopefully that will work.