If I use RelativeSource to get to the DataContext of the MainWindow, it doesn't work (binding errors about finding the source).
If I use ElementName to get to the DataContext of the MainWindow, same thing.
I also tried following this method to get at the correct DataContext: http://stackoverflow.com/questions/15033522/wpf-contextmenu-woes-how-do-i-set-the-datacontext-of-the-contextmenu.
See link below for a sample project showing my issue. I have placed a breakpoint on the command, so all you need to do is left-click the "convert to" button to get the context menu to appear and then click "to text". The breakpoint should be reached if the binding is correct. The method that is used in the attached project uses the stackoverflow method, and doesn't have any binding errors, but the command is still not reached.
http://1drv.ms/1F2k5ZH
Thanks for the help!
Jon
Hi Jon,
While the above solution might work for the MS ContextMenu, the Infragistics one has an extra layer between the context menu and the placement target which interferes with things. This extra layer means that the PlacementTarget property of the XamContextMenu is not actually set for you. There is another property called PlacementTargetResolved which contains that placement target information. This property is not a DependencyProperty and it does not implement INotifyPropertyChanged so binding to it is not helpful. You will only see null from this because when the binding is initially evaluated, the PlacementTargetResolved is null. Even when the value is changed later on, the binding won't update and you'll still be bound to null.
To get around this I wrote a very simple behavior that you can attach to the context menu. It simply handles the Opened event and takes the DataContext from the PlacementTargetResolved property and passes it to the XamContextMenu.
public class PassDataContextToContextMenu : Behavior<XamContextMenu> { protected override void OnAttached() { AssociatedObject.Opened += AssociatedObject_Opened; } protected override void OnDetaching() { AssociatedObject.Opened -= AssociatedObject_Opened; } void AssociatedObject_Opened(object sender, OpenedEventArgs e) { // when the context menu is opened, update the data context... Just in case the PlacementTargetResolved has changed FrameworkElement placementTarget = (FrameworkElement)AssociatedObject.PlacementTargetResolved; AssociatedObject.DataContext = placementTarget.DataContext; } }
<ig:XamContextMenu Placement="AlignedToTheRight"> <i:Interaction.Behaviors> <b:PassDataContextToContextMenu/> </i:Interaction.Behaviors> <ig:XamMenuItem Header="...to Text" Command="{Binding ConvertToTextCommand}"/> </ig:XamContextMenu>
Thank you for the quick reply!
I see that this works in the example I sent you. I found an error in my sample. What if the command is at the TheViewModelClass level? How could I pipe in the DataContext from there? That's what I really want. Sorry about the bad sample.
Thanks,
I found a solution by using {x:Reference ElementName} and then pulling the DataContext out in the ViewModel. Not the prettiest but it works. Thanks!
Could you maybe make use of the placement target's Tag property? All FrameworkElements have a Tag property so in the behavior you can just pass the placementTarget.Tag property to the AssociatedObject.Tag. Then inside the XamMenuItem, you can bind the CommandParameter property to the XamContextMenu's Tag property. A RelativeSource FindAncestor binding would do the trick there. This way your view model doesn't need to pull anything out of the datacontext. The command will only get passed the information it needs.
I thought I tried that. I think that since the Behavior you gave me modifies the DataContext on the ContextMenu, the Tag binding then is looking at the Command's DataContext (the outer context). I've got it working now so I won't mess with it for awhile. If you really want to make an example of doing it the way you suggest, you may. But I probably won't get around to testing it for a few weeks at least.
Thanks!
I've attached an updated version of your sample which uses the approach I just talked about. I took the command out of ItemViewModel and moved it to TheViewModelClass. I then setup the bindings in XAML such that when the button is clicked, it will fire the command in TheViewModelClass and pass it the ItemViewModel instance associated with where the button is located.
Awesome! Thanks for your help!