0

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 a DataGridCell.
  • 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, because Keyboard.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;
    }
}
mike
  • 1,627
  • 1
  • 14
  • 37

1 Answers1

0

If you do not need the focus on the test button I would just set Focusable="False" on btnTest1.

If you still want to have the button focusable, one idea could be not to use Keyboard.FocusedElement but the FocusManager.GetFocusedElement like this:

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 (FocusManager.GetFocusedElement(sender as DependencyObject) is DataGridCell) {
            //Process selected cell...
        }
    }

    public void OnCanExecute(object sender, CanExecuteRoutedEventArgs e)
    {
        e.CanExecute = (FocusManager.GetFocusedElement(sender as DependencyObject) is DataGridCell);
    }
}
S.Toniolo
  • 51
  • 1
  • 4