Andrzej
Andrzej

Reputation: 571

WPF shortcut in whole window

I'm developing MVVM application and I'd like to create global shortcuts for my application. It's necessary to navigatate application between modules. I have main window with code:

<UserControl.InputBindings>
    <KeyBinding Command="{Binding ChangeModuleComand}"
                    Key="M"
                    Modifiers="Control"/>
</UserControl.InputBindings>

It works when I have focus on my window,

But there is a problem when i have focus on TextBox in that UserControl (Command is not fired).

Is that possible to catch that key pressure without lots of modifications in whole application?

A.

Upvotes: 3

Views: 1793

Answers (2)

Ayo I
Ayo I

Reputation: 8124

Documenting this answer for others, as there is a much simpler way to do this that is rarely referenced, and doesn't require touching the XAML at all.

To link a keyboard shortcut at the Window level, in the Window constructor simply add a new KeyBinding to the InputBindings collection. As the command, pass in your arbitrary command class that implements ICommand. For the execute method, simply implement whatever logic you need. In my example below, my WindowCommand class takes a delegate that it will execute whenever invoked. When I construct the new WindowCommand to pass in with my binding, I simply indicate in my initializer, the method that I want the WindowCommand to execute.

You can use this pattern to come up with your own quick keyboard shortcuts.

public YourWindow() //inside any WPF Window constructor
{
   ...
   //add this one statement to bind a new keyboard command shortcut
   InputBindings.Add(new KeyBinding( //add a new key-binding, and pass in your command object instance which contains the Execute method which WPF will execute
      new WindowCommand(this)
      {
         ExecuteDelegate = TogglePause //REPLACE TogglePause with your method delegate
      }, new KeyGesture(Key.P, ModifierKeys.Control)));
   ...
}

Create a simple WindowCommand class which takes an execution delegate to fire off any method set on it.

public class WindowCommand : ICommand
{
    private MainWindow _window;

    //Set this delegate when you initialize a new object. This is the method the command will execute. You can also change this delegate type if you need to.
    public Action ExecuteDelegate { get; set; }

    //You don't have to add a parameter that takes a constructor. I've just added one in case I need access to the window directly.
    public WindowCommand(MainWindow window)
    {
        _window = window;
    }

    //always called before executing the command, mine just always returns true
    public bool CanExecute(object parameter)
    {
        return true; //mine always returns true, yours can use a new CanExecute delegate, or add custom logic to this method instead.
    }

    public event EventHandler CanExecuteChanged; //i'm not using this, but it's required by the interface

    //the important method that executes the actual command logic
    public void Execute(object parameter)
    {
        if (ExecuteDelegate != null) //let's make sure the delegate was set
        {
            ExecuteDelegate();
        }
        else
        {
            throw new InvalidOperationException("ExecuteDelegate has not been set. There is no method to execute for this command.");
        }
    }
}

I'm sure this can be used for other controls as well, but haven't tried it.

Upvotes: 1

Vynos
Vynos

Reputation: 146

It sounds like your event is being handled before it bubbles up that far. Next I am wondering if you could move your input binding up to your window rather than a user control. My final option would be to remove your input bindings and create a global check in code.

Since you're already working with input bindings, I have added the code option below:

    //In App.xaml.cs
    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);
        EventManager.RegisterClassHandler(typeof(UIElement), UIElement.KeyDownEvent, new RoutedEventHandler(GlobalClick));
    }

    private void GlobalClick(object sender, RoutedEventArgs e)
    {
        var args = (KeyEventArgs) e;
        if (args.KeyboardDevice.IsKeyDown(Key.M) &&
            args.KeyboardDevice.IsKeyDown(Key.LeftCtrl) || args.KeyboardDevice.IsKeyDown(Key.RightCtrl))
            MessageBox.Show("Works");
    }

Upvotes: 4

Related Questions