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.
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 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
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.
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}}"
Feel free to suggest an alternate approach to my problem if this isn't the best way to go about it.
I have a ToolBarTray and a TreeView. The tree has 2 items that are Concrete1 and Concrete2 classes respectively. I want the toolbar to have a different menu depending on which type of item is selected in the tree.
<ToolBarTray DockPanel.Dock="Top">
<ToolBar>
<ToolBar.Style>
<Style TargetType="{x:Type ToolBar}">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=tree, Path=SelectedItem, Converter={StaticResource convert}}" Value="{x:Type root:Concrete1}">
<!--what do i do here??-->
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=tree, Path=SelectedItem, Converter={StaticResource convert}}" Value="{x:Type root:Concrete2}">
<!--what do i do here??-->
</DataTrigger>
</Style.Triggers>
</Style>
</ToolBar.Style>
<ToolBar.Resources>
<Menu x:Key="awd">
<MenuItem Header="AWD"></MenuItem>
</Menu>
<Menu x:Key="dwa">
<MenuItem Header="DWA"></MenuItem>
</Menu>
</ToolBar.Resources>
</ToolBar>
</ToolBarTray>
<TreeView x:Name="tree" ItemsSource="{Binding Families}"></TreeView>
I've gotten as far as being able to set any regular property (background, etc) of the toolbar depending on which item is selected. I don't know how to proceed to add specific Menus to the toolbar.
I think I've figured it out. I've only done this with a ContextMenu and outside of a style trigger but I imagine it would be the same principle.
<TreeView>
<TreeView.ContextMenu>
<ContextMenu>
<StaticResourceExtension ResourceKey="myMenuItem"></StaticResourceExtension>
</ContextMenu>
</TreeView.ContextMenu>
</TreeView>