The issue is that the DataContext of the ContextMenu (i.e. where it's looking to bind the New command) is the tree-view node, not the tree view itself. Great if you've got commands related to the node - editing, moving, changing settings.
Not so good for the few that are pan-node like adding and deleting.
As it's looking in the node's DataContext (and no nodes exit) it can't find the command (and it doesn't make sense for it to be there anyway, as the object that manages the TreeView should be creating new items, not the items themselves).
The solution is to bind to a New command that's not in the DataContext of the item, but the TreeView. There's the frustration of dealing with data-binding with ContextMenu... as it's not in the same visual tree as the rest of the window it's often frustrating to deal with.
A solution is to reference the PlacementTarget of the context menu like this:
<TreeView Name="myTreeView" Width="200px">
<TreeView.ContextMenu>
<ContextMenu>
<MenuItem Header="Edit (This command exists in the Node's ViewModel)" Command="{Binding Edit}"/>
<MenuItem Header="New (This command exists in the Window's ViewModel)" Command="{Binding PlacementTarget.DataContext.New, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}}}"/>
</ContextMenu>
</TreeView.ContextMenu>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate>
<TextBlock Text="{Binding Path=Title}" />
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
Further questions
An example of adding a command as a static resource (Change Window to UserControl if you're in a view that's a UserControl):
<Window.Resources>
<local:MyCommand x:Key="MyCommand"/>
</Window.Resources>
Then referenced with:
<MenuItem Header="MyCommand" Command="{StaticResource MyCommand}"/>
Binding to your commands in the ViewModel (i.e. DataContext) is done like in the first example. In the same way you bind the Title
, you can bind to any property, such as an ICommand.
So for a view:
<MenuItem Header="New" Command="{Binding New}"/>
The View Model has a property NewCommand named New:
public NewCommand New { get; private set; }
People often use this because they have a generic ICommand that takes a delegate so they can configure all the actions that relate to that ViewModel. For example:
public class MyCommand : ICommand
{
public event EventHandler CanExecuteChanged;
public Action<object> Action { get; set; }
public MyCommand(Action<object> action)
{
Action = Action;
}
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
Action(parameter);
}
}
Then in the ViewModel, instead of having loads of ICommand classes all implemented, we can just re-use this and get it to do different things:
public MyCommand New { get; private set; }
public MyCommand Delete { get; private set; }
public MyCommand ClearAll { get; private set; }
public MyViewModelConstructor()
{
New = new MyCommand((parameter) =>
{
//Add new object
});
Delete = new MyCommand((parameter) =>
{
//Delete object
});
ClearAll = new MyCommand((parameter) =>
{
//Clear all objects
});
}