Bookmark and Share Share...    Subscribe to this feed Feed   About Christian Moser  


How to Solve Execution Problems of RoutedCommands in a WPF ContextMenu

The Problem

I recently run into a problem, with RoutedCommands in a ContextMenu. The problem was, that the commands could not be executed, even if the CommandBinding on the parent window allowed it.

The following example shows the problem with simple window that has a Menu and a ContextMenu on it. Both menus contains a MenuItem with a "Cut" command set.

 
<Window x:Class="RoutedCommandsInPopups.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">
    <StackPanel x:Name="stack" Background="Transparent">
        <StackPanel.ContextMenu>
            <ContextMenu>
                <MenuItem Header="Cut" Command="Cut" />
            </ContextMenu>
        </StackPanel.ContextMenu>
        <Menu>
            <MenuItem Header="Edit" >
                <MenuItem Header="Cut" Command="Cut" />
            </MenuItem>
        </Menu>
    </StackPanel>
</Window>
 
 

In the codebehind of the Window I added a CommandBinding to handle the "Cut" command.

 
public Window1()
{
    InitializeComponent();
 
    CommandBindings.Add(
        new CommandBinding(ApplicationCommands.Cut, CutExecuted, CanCut));
}
 
private void CutExecuted(object sender, ExecutedRoutedEventArgs e)
{
    MessageBox.Show("Cut Executed");
}
 
private void CanCut(object sender, CanExecuteRoutedEventArgs e)
{
    e.CanExecute = true;
}
 
 

The Reason

The reason is, that ContextMenus are separate windows with their own VisualTree and LogicalTree.

The reason is that the CommandManager searches for CommandBindings within the current focus scope. If the current focus scope has no command binding, it transfers the focus scope to the parent focus scope. When you startup your application the focus scope is not set. You can check this by calling FocusManager.GetFocusedElement(this) and you will receive null.

The Solution

Set the Logical Focus

The simplest solution is to initially set the logical focus of the parent window that is not null. When the CommandManager searches for the parent focus scope it finds the window and handels the CommandBinding correctly.

 
public Window1()
{
    InitializeComponent();
 
    CommandBindings.Add(
        new CommandBinding(ApplicationCommands.Cut, CutExecuted, CanCut));
 
    // Set the logical focus to the window
    Focus();
}
 
 

...or the same in XAML

 
<Window x:Class="RoutedCommandsInPopups.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    FocusManager.FocusedElement="
        {Binding RelativeSource={x:Static RelativeSource.Self}, Mode=OneTime}>
    ...
</Window>
 
 

Manually bind the CommandTarget

Another solution is to manually bind the CommandTarget to the parent ContextMenu.

 
<MenuItem Header="Cut" Command="Cut" CommandTarget="
          {Binding Path=PlacementTarget, 
          RelativeSource={RelativeSource FindAncestor, 
          AncestorType={x:Type ContextMenu}}}"/>
 
 




Last modified: 2009-06-17 12:26:26
Copyright (c) by Christian Moser, 2011.

 Comments on this article

Show all comments
alex
Commented on 26.January 2010
very useful. thanks.
a
Commented on 26.March 2010
ads
speed
Commented on 20.April 2010
Now that is the best idea I've read all day. Thank you for posting it.
Dario
Commented on 21.April 2010
Super!
thanks
Ed
Commented on 5.May 2010
At last! That's what I was looking for.
Andreas
Commented on 28.July 2010
Thank you for that, saved me a lot of time.
Aftab
Commented on 14.November 2010
Nice tutorial.really saved my time
a
Commented on 29.December 2010
did not help in my situation.
Kai
Commented on 20.April 2011
Thank you. Your post really helped me. I've been looking into this problem for 2 days, and your post answered my question.
serine
Commented on 6.June 2011
thanks :)

Name
E-Mail (optional)
Comment