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>
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(); }
<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 theCommandTarget
to the parent ContextMenu.
<MenuItem Header="Cut" Command="Cut" CommandTarget="
{Binding Path=PlacementTarget,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ContextMenu}}}"/>
No comments:
Post a Comment