I have a question regarding RoutedUICommand.OnCanExecute
and Keyboard.FocusedElement
.
I 've created a command with an InputGesture
(keyboard short-cut), assigned it to a MenuItem
or Button
and added it to the owning window's CommandBindings
with the following positive results:
- The buttons/menus are disabled, if the
Keyboard.FocusedElement
is not aDataGridCell
. - I can click on the menu and the command is executed, the focused
DataGridCell
remains focused. - I can use the keyboard short-cut and the command is executed, if the focus is on a
DataGridCell
.
What I am not able to resolve is the following issue:
- I cannot trigger the command by clicking on the button, because (apparently) the click causes
OnCanExecute
to fail, becauseKeyboard.FocusedElement
is then the button.
Strangely, though: if I disable the OnCanExecute
method by always setting e.CanExecute = true
, the above problem does not come up and within OnExecuted
Keyboard.FocusedElement
is indeed the DataGridCell
.
Of all the sites I visited in search for a solution I found https://blogs.msdn.microsoft.com/mikehillberg/2009/03/20/icommand-is-like-a-chocolate-cake/ very interesting (esp. ToolBar is a “focus scope”), but it wasn't enough to help me resolve my issue.
Here are the relevant (and abbreviated) code snippets.
A command that is supposed to work with a currently selected DataGridCell
:
public class MyDataGridCellCommand : RoutedUICommand
{
public MyDataGridCellCommand()
{
this.InputGestures.Add(new KeyGesture(Key.F1));
this.Text = "Process selected cell";
}
public void OnExecuted(object sender, ExecutedRoutedEventArgs e)
{
if(Keyboard.FocusedElement is DataGridCell cell){
//Process selected cell...
}
}
public void OnCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = (Keyboard.FocusedElement is DataGridCell);
}
}
The window's Xaml:
<Window x:Class="MainWindow" Title="MainWindow" Height="450" Width="800">
<Grid>
<StackPanel Orientation="Vertical" >
<Menu>
<MenuItem x:Name="menuTest"></MenuItem>
</Menu>
<StackPanel Orientation="Horizontal">
<!--Button in "default" stackpanel -->
<StackPanel Orientation="Horizontal" >
<Button x:Name ="btnTest1" Content="Test1"/>
</StackPanel>
<!--Button in "focus scope" stackpanel -->
<StackPanel Orientation="Horizontal" FocusManager.IsFocusScope="True" >
<Button x:Name ="btnTest2" Content="Test2"/>
</StackPanel>
<!--Button in toolbar-->
<ToolBar x:Name ="toolBar1" Focusable="True">
<Button x:Name ="btnTest3" Content="Test3" Margin="5,0,5,0"/>
</ToolBar>
</StackPanel>
<DataGrid>
<!--Layout Attributes removed for brevity. -->
</DataGrid>
</StackPanel>
</Grid>
</Window>
The window's code-behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var cmd = new MyDataGridCellCommand();
this.CommandBindings.Add(new CommandBinding(cmd, cmd.OnExecuted, cmd.OnCanExecute));
this.btnTest1.Command = cmd;
this.btnTest2.Command = cmd;
this.btnTest3.Command = cmd;
this.menuTest.Command = cmd;
this.DataContext = this;
}
}