Let's say I have a Window with a property returning a Command (in fact, it's a UserControl with a Command in a ViewModel class, but let's keep things as simple as possible to reproduce the problem).
The following works:
<Window x:Class="Window1" ... x:Name="myWindow">
<Menu>
<MenuItem Command="{Binding MyCommand, ElementName=myWindow}" Header="Test" />
</Menu>
</Window>
But the following does not work.
<Window x:Class="Window1" ... x:Name="myWindow">
<Grid>
<Grid.ContextMenu>
<ContextMenu>
<MenuItem Command="{Binding MyCommand, ElementName=myWindow}" Header="Test" />
</ContextMenu>
</Grid.ContextMenu>
</Grid>
</Window>
The error message I get is
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'ElementName=myWindow'. BindingExpression:Path=MyCommand; DataItem=null; target element is 'MenuItem' (Name=''); target property is 'Command' (type 'ICommand')
Why? And how do I fix this? Using the DataContext
is not an option, since this problem occurs way down the visual tree where the DataContext already contains the actual data being displayed. I already tried using {RelativeSource FindAncestor, ...}
instead, but that yields a similar error message.
If (like me) you have an aversion to ugly complex binding expressions, here is a simple code-behind solution to this problem. This approach still allows you to keep clean command declarations in your XAML.
XAML:
Code behind:
Hurray for web.archive.org! Here is the missing blog post:
The problem is that the ContextMenu it not in the visual tree, so you basically have to tell the Context menu about which data context to use.
Check out this blogpost with a very nice solution of Thomas Levesque.
He creates a class Proxy that inherits Freezable and declares a Data dependency property.
Then it can be declared in the XAML (on a place in the visual tree where the correct DataContext is known):
And used in the context menu outside the visual tree:
I found out it wasn't working for me due to the menu item being nested, which mean I had to traverse up an extra "Parent" to find the PlacementTarget.
A better way is to find the ContextMenu itself as the RelativeSource and then just bind to the placement target of that. Also since the tag is the window itself, and your command is in the viewmodel, you need to have the DataContext set as well.
I ended up with something like this
What this means is that if you end up with a complicated context menu with submenus etc.. you don't need to keep adding "Parent" to each levels Commands.
-- EDIT --
Also came up with this alternative to set a tag on every ListBoxItem that binds to the Window/Usercontrol. I ended up doing this because each ListBoxItem was represented by their own ViewModel but I needed the menu commands to execute via the top level ViewModel for the control, but pass their the list ViewModel as a parameter.
Based on HCLs answer, this is what I ended up using:
See this article from Justin Taylor for a workaround.
Update
Sadly, the referenced blog is no more available. I have tried to explain the proceeding in another SO-answer. It can be found here.