I want to bind a contextmenu to a list of commands.
<Grid.ContextMenu>
<ContextMenu ItemsSource="{Binding ItemContextCommands, Converter={StaticResource commandToStringConverter}}">
<ContextMenu.ItemTemplate >
<DataTemplate DataType="MenuItem">
<MenuItem Command="{Binding}"></MenuItem>
</DataTemplate>
</ContextMenu.ItemTemplate>
</ContextMenu>
</Grid.ContextMenu>
The commandToStringConverter simply converts a list of commands to a list of strings calling the ToString() on each command in the list.
How can I achieve that the Command in each MenuItem is called?
I would use a small "view model" to hold the informations for such a command.
class ContextAction : INotifyPropertyChanged
{
public string Name;
public ICommand Action;
public Brush Icon;
}
make a collection inside your view model which should get the context actions like
ObservableCollection<ContextAction> Actions {get;set;}
and simply bind this collection to your ContextMenu.
<Grid.ContextMenu>
<ContextMenu ItemsSource="{Binding Actions}" />
The ItemTemplate for the contextmenu items can now access the name, the command and whatever else you might need. It might be useful to change the CommandParameter as well so that it will call the command with the actions owning element, not with the action itself.
i use something like this:
public class ContextMenuVM
{
public string Displayname {get;set;}
public ICommand MyContextMenuCommand {get;set;}
}
in your contextmenu datacontext:
public ObservableCollection<ContextMenuVM> MyCommandList {get;set;}
in your xaml
<ContextMenu ItemsSource="{Binding MyCommandList}">
<ContextMenu.ItemTemplate >
<DataTemplate DataType="MenuItem">
<MenuItem Header="{Binding Displayname}" Command="{Binding MyContextMenuCommand}"></MenuItem>
</DataTemplate>
</ContextMenu.ItemTemplate>
</ContextMenu>
its written without ide, so maybe some syntax errors in there
An improved XAML version of #blindmils solution below:
<ContextMenu ItemsSource="{Binding MyCommandList}">
<ContextMenu.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Header" Value="{Binding Displayname}" />
<Setter Property="Command" Value="{Binding MyContextMenuCommand }" />
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
Related
I have a context menu on a button, which I have added in xaml as follows:
<Button Content="{Binding MaterialLabel}"
Command="{Binding SwapCommand}">
<Button.Resources>
<ContextMenu x:Key="RClickSwaps" ItemsSource="{Binding SwapList}">
<ContextMenu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Header" Value="{Binding targetMaterial.Component_Code}"/>
<Setter Property="Command" Value="{Binding RClickSwapCommand}"/>
<Setter Property="ToolTip">
<Setter.Value>
<ContentControl>
<Border CornerRadius="2" BorderThickness="2">
<TextBlock Text="{Binding targetMaterial.Component_Name}"/>
</Border>
</ContentControl>
</Setter.Value>
</Setter>
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
</Button.Resources>
</Button>
The Binding for the UserControl as a whole is to a view model which contains a collection (SwapList) of classes that contain the command and TargetMaterial properties for the context menu used above:
//Control View Model
public ObservableCollection<SwapMenuItem> SwapList { get; set; }
//... Some irrelevant Code excluded ...
SwapList = new ObservableCollection<SwapMenuItem>();
var swaps = new ObservableCollection<Raw_Materials>(await ComponentDataService.GET_MaterialSwaps(New_Material));
foreach (var item in swaps)
{
SwapList.Add(new SwapMenuItem(item, this));
}
OnPropertyChanged("SwapList");
The SwapMenuItem itself contains the following properties and a command that look a bit like this:
public class SwapMenuItem : Observable
{
public ComponentChangeViewModel ComponentChangeViewModel { get; set; }
public Raw_Materials TargetMaterial { get; set; }
public ICommand RClickSwapCommand {get;set;}
public SwapMenuItem(Raw_Materials raw_Materials, ComponentChangeViewModel componentChangeViewModel)
{
ComponentChangeViewModel = componentChangeViewModel;
TargetMaterial = raw_Materials;
}
private void Swap()
{
//The command actually calls this method, but I excluded the long winded code
}
}
My problem is that the context menu does not appear when I right click the button, nothing happens at all. I am trying to work within the MVVM pattern, but am I missing some sort of event to make the context menu appear on right click? Most of the advice online does not use the context menu as part of Button.Resources, but this seems to be the best way to do it according to some (for instance here). None of these examples contain anything to make the context menu appear on right click, so I figured it must be there by default?
Why can I not make the context menu appear, even when there are values in the ItemsSource collection?
You only define a context menu with a key in the resources. The Button does not automatically know that it should use it. You have to set this resource to the ContextMenu property of Button.
You can either use the StaticResource markup extension in element property syntax like this:
<Button Content="{Binding MaterialLabel}"
Command="{Binding SwapCommand}">
<Button.Resources>
<ContextMenu x:Key="RClickSwaps" ItemsSource="{Binding SwapList}">
<!-- ...your context menu markup. -->
</ContextMenu>
</Button.Resources>
<Button.ContextMenu>
<StaticResource ResourceKey="RClickSwaps"/>
</Button.ContextMenu>
</Button>
Or you can set it in attribute syntax by using the DynamicResource markup extension. Note that StaticResource does not work here, as the resource RClickSwaps is defined later in XAML, so it cannot be resolved statically at this point.
<Button Content="{Binding MaterialLabel}"
Command="{Binding SwapCommand}"
ContextMenu="{DynamicResource RClickSwaps}">
<Button.Resources>
<ContextMenu x:Key="RClickSwaps" ItemsSource="{Binding SwapList}">
<!-- ...your context menu markup. -->
</ContextMenu>
</Button.Resources>
</Button>
Of course you can also just assign the context menu directly, without Resources.
<Button Content="{Binding MaterialLabel}"
Command="{Binding SwapCommand}">
<Button.ContextMenu>
<ContextMenu ItemsSource="{Binding StringItems}">
<!-- ...your context menu markup. -->
</ContextMenu>
</Button.ContextMenu>
</Button>
In my WPF MVVM (using Prism) application I have a listview. In this listview I have bound a Ctrl-C key input to a copy function with an IList parameter. (see CopyChannelChangeNotificationToClipboardCommand)
This works well.
Now I want to add a context menu that has a MenuItem with "Copy Ctrl-C" functionality.
What I do not understand is how to bind the CommandParameter from the MenuItem so that it provides the ListView's Selected items? The idea is to use the same function as the input bound key, i.e. get the IList from the listview to be attached as a command parameter.
I've tried to read quite a few posts here, tried even more but all result in the objList parameter to be null.
If anyone has suggestions on how to accomplish this I'd be grateful.
My view (partial)
<ListView Grid.Row="1" ItemTemplate="{StaticResource ChannelChangeDataTemplateKey}" ItemsSource="{Binding ChannelChangeNotifications}" lb:ListBoxBehavior.ScrollOnNewItem="true">
<ListView.InputBindings>
<KeyBinding Key="C" Modifiers="Control" Command="{Binding CopyChannelChangeNotificationToClipboardCommand}" CommandParameter="{Binding Path=SelectedItems, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListView}}}"/>
</ListView.InputBindings>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.ContextMenu>
<ContextMenu>
<MenuItem Header="{x:Static p:Resources.UICopySelectedItems}" Command="{Binding CopySelectedItemsClickCommand}" CommandParameter="{Binding Path=SelectedItems, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListView}}}"></MenuItem>
</ContextMenu>
</ListView.ContextMenu>
</ListView>
and my view model code
public Prism.Commands.DelegateCommand<IList> CopyChannelChangeNotificationToClipboardCommand => new Prism.Commands.DelegateCommand<IList>(CopyChannelChangeNotificationToClipboard);
public Prism.Commands.DelegateCommand<IList> CopySelectedItemsClickCommand => new Prism.Commands.DelegateCommand<IList>(CopyChannelChangeNotificationToClipboard);
private void CopyChannelChangeNotificationToClipboard(IList objList)
{
if (objList != null)
{
// Copy selected list view objects to clipboard
// Works well when coming from CopyChannelChangeNotificationToClipboardCommand
// but not when coming from CopySelectedItemsClickCommand
// since objList is always null
}
}
I have a simple TreeView that I'm trying to create a custom DataTemplate for. It displays as desired, but when I attempt to select the TreeViewItem, the highlighting does not work if I click the text in the TreeView. However, if I select just to the left of the text, it works:
The source is pretty straight forward, so I'm guessing I'm just missing a styling connection:
xaml
<TreeView x:Name="treeView"
ItemsSource="{Binding TreeViewItems}"
Grid.Row="0">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Path=MenuItems}">
<TreeViewItem Header="{Binding Header}">
<TreeViewItem.InputBindings>
<MouseBinding MouseAction="LeftClick"
Command="{Binding Command}" />
</TreeViewItem.InputBindings>
</TreeViewItem>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
I've tried adding this to the xaml, but it didnt help:
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}"
BasedOn="{StaticResource {x:Type TreeViewItem}}" />
</TreeView.ItemContainerStyle>
TreeViewModel
public class TreeViewModel : BaseNotifyModel, ITreeViewModel
{
public TreeViewModel(ITreeViewService menuService)
{
TreeViewItems =
new ReadOnlyObservableCollection<MenuItemViewModel>(menuService.TreeViewMenu);
}
public ReadOnlyObservableCollection<MenuItemViewModel> TreeViewItems
{
get
{
return Get<ReadOnlyObservableCollection<MenuItemViewModel>>();
}
private set
{
Set(value);
}
}
}
MenuItemViewModel
public class MenuItemViewModel : BaseNotifyModel
{
public MenuItemViewModel()
{
MenuItems =
new ObservableCollection<MenuItemViewModel>();
}
public String Header
{
get
{
return Get<String>();
}
set
{
Set(value);
}
}
public ICommand Command
{
get
{
return Get<ICommand>();
}
set
{
Set(value);
}
}
public ObservableCollection<MenuItemViewModel> MenuItems
{
get
{
return Get<ObservableCollection<MenuItemViewModel>>();
}
set
{
Set(value);
}
}
}
The TreeView is creating a TreeViewItem for each item in ItemsSource, so don't nest another TreeViewItem inside the one that the TreeView already created for you. That doesn't serve any purpose. Your template should just be providing a way for the existing TreeViewItem to display whatever's in its DataContext (your MenuItemViewModel, in this case).
You want to display the Header property in the tree view item; so just do that. Nothing fancy, just a Label or ContentControl, or even a TextBlock if it's String (though it's a lot of fun in WPF to have the flexibility of arbitrary content). When the user clicks on the content, your command executes. The only thing the user can see in the tree is the content. That's the only visible part of the TreeViewItem item, so that's what the user is going to click on.
Problem number two: Once the input binding is getting LeftClick, that breaks selection in the TreeView. It appears to me that you can't get there from here by that method.
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Path=MenuItems}">
<ContentControl
Content="{Binding Header}"
Background="Transparent"
>
<ContentControl.InputBindings>
<!-- This invokes the command, but breaks selection -->
<MouseBinding MouseAction="LeftClick"
Command="{Binding Command}" />
</ContentControl.InputBindings>
</ContentControl>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
Here's what you can do:
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Path=MenuItems}">
<ContentControl
Content="{Binding Header}"
Background="Transparent"
>
</ContentControl>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
<TreeView.ItemContainerStyle>
<Style TargetType="TreeViewItem" BasedOn="{StaticResource {x:Type TreeViewItem}}">
<EventSetter Event="Selected" Handler="MenuTreeViewItem_Click" />
</Style>
</TreeView.ItemContainerStyle>
Codebehind
private void MenuTreeViewItem_Click(object sender, RoutedEventArgs e)
{
((MenuItemViewModel)((FrameworkElement)sender).DataContext).Command.Execute(null);
}
There is a way to bind an event to a command in pure XAML, but it requires some C# code (LOL). But I mean, it's "pure XAML" in the sense that it's a nice generalized reusable attached behavior, not an unsightly event handler in your codebehind. Instead, it does exactly what I did above, but it does it in code that you can more easily avert your eyes from, and that you can reuse in pure XAML.
With help from #Ed Plunket and #Evk, I found a solution that will work. I switched to a ContentPresenter and used the Interaction.Triggers to call the command on the MouseLeftButtonUp action.
<TreeView ItemsSource="{Binding TreeViewItems}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Path=MenuItems}">
<ContentPresenter Content="{Binding Header}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonUp">
<i:InvokeCommandAction Command="{Binding Path=DataContext.Command, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TreeViewItem}}}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ContentPresenter>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
I'm using MVVM and I want to data bind my list of MenuViewModels to my maim menu. Which consists of a set of menu items and separators.
Here's my MenuItemViewModel code:
public interface IMenuItemViewModel
{
}
[DebuggerDisplay("---")]
public class SeparatorViewModel : IMenuItemViewModel
{
}
[DebuggerDisplay("{Header}, Children={Children.Count}")]
public class MenuItemViewModel : IMenuItemViewModel, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public MenuItemViewModel(string header, ICommand command, ImageSource imageSource)
{
Header = header;
Command = command;
ImageSource = imageSource;
Children = new List<IMenuItemViewModel>();
}
public string Header { get; private set; }
public ICommand Command { get; private set; }
public ImageSource ImageSource { get; private set; }
public IList<IMenuItemViewModel> Children { get; private set; }
}
And my Main window looks like this:
<Window.Resources>
<HierarchicalDataTemplate DataType="{x:Type ViewModel:MenuItemViewModel}"
ItemsSource="{Binding Children}">
<MenuItem Header="{Binding Header}"
Command="{Binding Command}"/>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type ViewModel:SeparatorViewModel}">
<Separator />
</DataTemplate>
</Window.Resources>
<DockPanel>
<Menu DockPanel.Dock="Top"
ItemsSource="{Binding MenuItems}">
</Menu>
</DockPanel>
Should be very simple stuff. Unfortunately, either the menu item looks wrong or the separator is an empty menuItem (depending on what I've tried).
So, how do I get my Menu to find my two DataTemplates?
Solved my own question
After spending several hours searching the web, I found lots of examples that work against the WPF's natural intentions but none that worked with it.
Here's how to work with the Menu control and not against it...
A little Background
WPF's Menu control will normally auto create MenuItem objects for you when it is binded to a POCO collection, using the ItemsSource property.
However, this default behavior can be overridden! Here's how...
The Solution
First, you must create a class that derives from ItemContainerTemplateSelector. Or use the simple class I've created:
public class MenuItemContainerTemplateSelector : ItemContainerTemplateSelector
{
public override DataTemplate SelectTemplate(object item, ItemsControl parentItemsControl)
{
var key = new DataTemplateKey(item.GetType());
return (DataTemplate) parentItemsControl.FindResource(key);
}
}
Second, you must add a reference to the MenuItemContainerTemplateSelector class to your Windows resources object, like so:
<Window.Resources>
<Selectors:MenuItemContainerTemplateSelector x:Key="_menuItemContainerTemplateSelector" />
Third, you must set two properties (UsesItemContainerTemplate, and ItemContainerTemplateSelector) on both the Menu and the MenuItem (which is defined in the HierarchicalDataTemplate).
Like so:
<HierarchicalDataTemplate DataType="{x:Type ViewModel:MenuItemViewModel}"
ItemsSource="{Binding Children}">
<MenuItem Header="{Binding Header}"
Command="{Binding Command}"
UsesItemContainerTemplate ="true"
ItemContainerTemplateSelector=
"{StaticResource _menuItemContainerTemplateSelector}"/>
</HierarchicalDataTemplate>
<Menu DockPanel.Dock="Top"
ItemsSource="{Binding MenuItems}"
UsesItemContainerTemplate="True"
ItemContainerTemplateSelector=
"{StaticResource _menuItemContainerTemplateSelector}">
</Menu>
Why it Works
For optimization purposes, the Menu uses the UsesItemContainerTemplate flag (which has a default value of false) to skip the DataTemplate lookup and just returns a normal MenuItem object. Therefore, we needed to set this value to true and then our ItemContainerTemplateSelector works as expected.
Happy Coding!
A solution without the TemplateSelector:
provide ItemContainerTemplates instead of the DataTemplates :
<ContextMenu ItemsSource="{Binding Path=MenuItems}" UsesItemContainerTemplate="True">
<ContextMenu.Resources>
<ResourceDictionary>
<ItemContainerTemplate DataType="{x:Type ViewModel:MenuItemViewModel }">
<MenuItem Header="{Binding Path=Header}" Command="{Binding Path=Command}" UsesItemContainerTemplate="True">
<MenuItem.Icon>
<Image Source="{Binding Path=ImageSource}"/>
</MenuItem.Icon>
</MenuItem>
</ItemContainerTemplate>
<ItemContainerTemplate DataType="{x:Type ViewModel:SeparatorViewModel}">
<Separator >
<Separator.Style>
<Style TargetType="{x:Type Separator}" BasedOn="{StaticResource ResourceKey={x:Static MenuItem.SeparatorStyleKey}}"/>
</Separator.Style>
</Separator>
</ItemContainerTemplate>
</ResourceDictionary>
</ContextMenu.Resources>
</ContextMenu>
Notes:
I haven't tried Children
the separator styled wrong: I had to manually re-apply the style
Another approach is to:
have a Boolean property on your menu item ViewModel that indicates whether an item is a separator or not
use a trigger based on this property to change the ControlTemplate of the MenuItem so that it uses a Separator control instead
Like so:
<Menu ItemsSource="{Binding MenuItems}">
<Menu.Resources>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Header" Value="{Binding Header}" />
<Setter Property="Command" Value="{Binding Command}" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsSeparator}" Value="True">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type MenuItem}">
<Separator />
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
<HierarchicalDataTemplate DataType="{x:Type ViewModel:MenuItemViewModel}"
ItemsSource="{Binding Children}" />
</Menu.Resources>
</Menu>
I have a TreeView with multiple HierarchicalDataTemplate & DataTemplate items and I'm using Caliburn Micro for mvvm. The ItemsSource for the treeview is pointing to a collection in the viewmodel called 'TreeData' and I tried adding a specific ContextMenu for each HierarchicalDataTemplate & DataTemplate.
In the ContextMenu I use the caliburn functionality "cal:Message.Attach" to call a function in the
I made a smaller example of the treeview to illustrate the problem.
In the ViewModel (the collection object):
public class MyViewModel
{
// TreeData object
public ObservableCollection<TestRoot> TreeData = new ObservableCollection<TestRoot>()
{
new TestRoot()
{
Name = "Root item"
}
};
// the function on the viewmodel that should be called
public void DoSomething(object item)
{
MessageBox.Show("MyViewModel - DoSomething called");
}
}
The collection object:
public class TestRoot
{
public string Name { get; set; }
// caliburn calls this instead of the one on the viewmodel
public void DoSomething(object item)
{
MessageBox.Show("TestRoot - DoSomething called");
}
}
MyView.xaml treeview with only one (Hierarchical)DataTemplate:
<TreeView Margin="5" ItemsSource="{Binding TreeData}">
<TreeView.Resources>
<DataTemplate DataType="{x:Type vm:TestRoot}" >
<StackPanel Orientation="Horizontal">
<StackPanel.ContextMenu>
<ContextMenu>
<!-- caliburn (?) chooses the method on the collection object, not the viewmodel -->
<MenuItem Header="test dosomething" cal:Message.Attach="DoSomething($dataContext)"></MenuItem>
</ContextMenu>
</StackPanel.ContextMenu>
<TextBlock Text="{Binding Name}"/>
</StackPanel>
</DataTemplate>
</TreeView.Resources>
</TreeView>
In another piece of code, I placed the ContextMenu in the TreeView.ContextMenu. There it worked as it 'should', pointing to the method on the viewmodel.
Looking around for a solution, I see things like "inheritance context". I think it might have something to do with it, but I'm not sure. How can I tell caliburn it must look in the viewmodel for my method, instead of the item in the TreeView I clicked on?
Or is there another possibility? For example: defining the different ContextMenus in the Resources and pointing them to the DataTemplates? But, wont that cause the exact same problem?
Please note that I'd like to keep the code-behind as minimal as possible. Thanks
update
For the completeness, here's the real development code. This should be right, no?
<TreeView ItemsSource="{Binding OrderTreeViewData.OrderTreeViewCategories}"
cal:Message.Attach="[Event SelectedItemChanged] = [Action OnSelectedItemChanged($this)]">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<!-- We have to select the item which is right-clicked on -->
<EventSetter Event="TreeViewItem.PreviewMouseRightButtonDown"
Handler="TreeViewItem_PreviewMouseRightButtonDown"/>
<!-- set expanded -->
<Setter Property="TreeViewItem.IsExpanded" Value="True"/>
</Style>
</TreeView.ItemContainerStyle>
<TreeView.Resources>
<!-- dredge nodes -->
<HierarchicalDataTemplate DataType="{x:Type programs:DredgeRoot}"
ItemsSource="{Binding Dredgezones}">
<StackPanel>
<StackPanel.ContextMenu>
<ContextMenu DataContext="{Binding Path=PlacementTarget, RelativeSource={RelativeSource Self}}">
<MenuItem Header="Add dredge zone" cal:Message.Attach="TreeViewAddDredgeZone($datacontext)"></MenuItem>
</ContextMenu>
</StackPanel.ContextMenu>
<TextBlock Text="{Binding Name}"/>
</StackPanel>
</HierarchicalDataTemplate>
<!-- omitted other templates -->
</TreeView.Resources>
</TreeView>
Unfortunately there is still one tricky part to deal with. Due to the specific Popup behavior it doesn't inherit DataContext. To access proper context you have to get the PlacementTarget:
<StackPanel Tag="{Binding DataContext, RelativeSource={RelativeSource Self}}">
<StackPanel.ContextMenu>
<ContextMenu DataContext="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
<MenuItem Header="test dosomething" cal:Message.Attach="DoSomething"/>
</ContextMenu>
</StackPanel.ContextMenu>
</StackPanel>
I think you have the nearly same problem as me, maybe see this topic : Bind contextMenu to a different viewmodel from treeview
You can try to use a command :
Try to change you code to :
<ContextMenu x:Key="MyContextMenu">
<MenuItem Header="Add dredge zone" Command="{Binding PlacementTarget.Tag.DataContext.TreeViewAddDredgeZoneCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}}"
CommandParameter="{Binding}"></MenuItem>
</ContextMenu>
Then add to your hierarchicalDataTemplate a Tag and ContextMenu
<HierarchicalDataTemplate DataType="{x:Type programs:DredgeRoot}"
ItemsSource="{Binding Dredgezones}" Tag="{Binding RelativeSource={RelativeSource AncestorType=UserControl}} ContextMenu="{StaticResource MyContextMenu}">
And in your viewmodel you can add your command with something like this :
public ICommand TreeViewAddDredgeZoneCommand
{
//your code here
}