|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
How to Solve Execution Problems of RoutedCommands in a WPF ContextMenuThe ProblemI 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 ReasonThe 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 The SolutionSet the Logical FocusThe 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 CommandTargetAnother solution is to manually bind the
<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 articleShow all comments
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||