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.
Related
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
Okay, guys. I've been trying this for about 3 days now, and no amount of Googling is helping. Below is a snippet of my XAML (should be enough to follow along).
My problem is the command for the "ContextMenu".
As you can see I have DeleteTagCommand. Now that command works if I throw it in the position of CheckBoxCommand, which is great.. But it just will be called in it's current location, and it's driving me insane.
<ScrollViewer Grid.Column="0">
<StackPanel Orientation="Vertical">
<ItemsControl ItemsSource="{Binding Tags, UpdateSourceTrigger=PropertyChanged}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Value}" Margin="10,5,10,5" Command="{Binding DataContext.CheckBoxCommand,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type Grid}}}"
CommandParameter="{Binding }">
<CheckBox.ContextMenu>
<ContextMenu>
<MenuItem Header="Delete" Command="{Binding DataContext.DeleteTagCommand,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type Grid}}}"
CommandParameter="{Binding}" />
</ContextMenu>
</CheckBox.ContextMenu>
</CheckBox>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ScrollViewer>
I've tried:
Calling 'ElementName' from all over the show, but it's never being picked up
Changing the 'AncestorLevel' to obscene numbers, hoping that was the problem
And more..
Not sure what would be useful for you guys, but below is the output message I get
Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.Grid', AncestorLevel='1''. BindingExpression:Path=DataContext.DeleteTagCommand; DataItem=null; target element is 'MenuItem' (Name=''); target property is 'Command' (type 'ICommand')
Thanks
ContextMenus aren't actually part of the same visual tree as their parents so they can't directly bind to any elements within it. They can, however, still bind to StaticResources. The trick is to thus use an intermediate proxy such as the BindingProxy class shown on this page. Start by adding an instance to your ItemsControl resource block:
<ItemsControl.Resources>
<local:BindingProxy x:Key="Proxy" Data="{Binding}" />
</ItemsControl.Resources>
Then use it to bind your ContextMenu command:
<ContextMenu>
<MenuItem Header="Delete" Command="{Binding Data.DeleteTagCommand, Source={StaticResource Proxy}}" CommandParameter="{Binding}" />
</ContextMenu>
I am using the Command pattern with (among other things) a context menu on a TreeViewItem, which uses HierarchicalDataTemplates. The MainWindowViewModel (a static resource for the Window) has properties that expose singleton objects that in turn have properties that represent the commands. I am able to execute the command just fine, but some of the commands need to pass the TreeViewItem's DataContext as the CommandParameter.
Here's a specific example:
One node of the tree has the ItemsSource bound to an ObservableCollection of individual AnalysisMain objects. Each of the resulting subnodes has a ContextMenu (with a DataContext bound to the AnalysisController) which has (among others) a Remove MenuItem. The Remove MenuItem's Command property is bound to the CommandRemove command on the AnalysisController singleton object (and it executes just fine). But this also requires the CommandParameter to be bound to the AnalysisMain object that serves as the DataContext for the subnodes in the tree. I have tried using RelativeSource with the AncestorType set to TreeViewItem and the Path set to DataContext:
<HierarchicalDataTemplate DataType="{x:Type vmAnalysis:AnalysisMain}">
<WrapPanel Orientation="Horizontal">
<Image Source="Analysis\Icon_Analysis_Main_16_Normal.png" Margin="0,0,2,0" Width="16"/>
<TextBlock Text="{Binding TreeViewTitle}">
<TextBlock.ContextMenu>
<ContextMenu DataContext="{StaticResource mainWindowViewModel}">
<MenuItem Header="Remove" Command="{Binding Path=AnalysisController.CommandRemove}"
CommandParameter="{Binding RelativeSource={RelativeSource AncestorType={x:Type TreeViewItem}, AncestorLevel=4}, Path=DataContext}">
</MenuItem>
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
</WrapPanel>
</HierarchicalDataTemplate>
Without the AncestorLevel set, when I open the ContextMenu, I get the following in the Output window:
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.TreeViewItem', AncestorLevel='4''. BindingExpression:Path=DataContext; DataItem=null; target element is 'MenuItem' (Name=''); target property is 'CommandParameter' (type 'Object')
I have tried several values for the AncestorLevel to no avail.
From examining the Visual Tree in Christian Mosers WPF Inspector, I don't see the context menu in the visual tree. Although the TreeViewItem shows the DataContext object. I just need a way to "navigate" to it in order to bind to it.
As an alternative, I have tried leaving the ContextMenu DataContext alone and setting the Command Binding's Source to point back to the AnalysisController. This also works for executing the command, but I am not able to bind to the TreeViewItem's DataContext for the CommandParameter:
<ContextMenu>
<MenuItem Header="Remove" Command="{Binding Source={StaticResource mainWindowViewModel}, Path=AnalysisController.CommandRemove}"
CommandParameter="{Binding Source={RelativeSource Self}, Path=DataContext}">
</MenuItem>
</ContextMenu>
I have also tried just using CommandParameter="{Binding}", which also doesn't work. (In both cases, I just get null sent as the parameter. No warning / error is written to the Output window.) EDIT: For anyone else with this problem, the second option was doomed from the beginning, because I mistakenly put in Source={RelativeSource Self}, which would refer to the MenuItem and not the TreeViewItem. However, changing this with AncestorType / AncestorLevel makes no difference.
You have to use PlacementTarget property of the ContextMenu
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu
DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
</ContextMenu>
</Setter.Value>
</Setter>
And on your MenuItem do a RelativeSource to ContextMenu then use PlacementTarget.DataContext as your binding to your CommandParameter
I would like to reuse a ContextMenu on several DataGrid.
So I placed the context menu in the Resources of my Window.
I have trouble to bind to the SelectedItem property of the DataGrid on which the ContextMenu is placed.
In this example, I'm trying to have the Name property of the SelectedItem displayed in the context menu.
<Window.Resources>
<ContextMenu x:Key="DgContextMenu"
DataContext="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
<MenuItem Header="{Binding SelectedItem.Name, RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
</ContextMenu>
</Window.Resources>
<DataGrid ItemsSource="{Binding CollectionView}"
ContextMenu="{StaticResource DgContextMenu}"
Tag="{Binding DataContext, RelativeSource={RelativeSource Self}}">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Name" Binding="{Binding Name}" />
</DataGrid.Columns>
</DataGrid>
Thanks in advance
The way you have written your example has binding error and that's why your context menu doesn't work. You have binded menu item header to SelectedItem.Name of ContextMenu object which doesn't have SelectedItem property (you can tell that from RelativeSource part of the menu item binding). One possible solution, among others, would be to bind DataContext of ContextMenu to DataGrid through PlacementTarget (not PlacementTarget.Tag). Since child controls „inherit“ DataContext of the parent you can just specify Path in the menu item binding. This is how it would look:
<Window.Resources>
<ContextMenu x:Key="DgContextMenu"
DataContext="{Binding Path=PlacementTarget, RelativeSource={RelativeSource Self}}">
<MenuItem Header="{Binding Path=SelectedItem.Name}" />
</ContextMenu>
</Window.Resources>
<DataGrid ItemsSource="{Binding CollectionView}"
ContextMenu="{StaticResource DgContextMenu}"
>
</DataGrid>
Basically you can find those errors if you run application in VS debugger and watch output in Output window (Debug -> Window -> Output). In output window you should look for System.Windows.Data Error line and in that line you will see the type of an object and property you are trying to bind and that will give you a clue what's wrong with your binding in XAML.
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.