I want to implement Keyboard shortcuts for a MenuItem
. I have used the code below:
The best way to do this is to make a Command
, and associate the InputGesture
with that command:
public static class Commands
{
public static readonly RoutedCommand CreateNew = new RoutedCommand("New", typeof(Commands));
static Commands()
{
SomeCommand.InputGestures.Add(new KeyGesture(Key.N, ModifierKeys.Control));
}
}
...
// Wherever you want to create the MenuItem. "local" should be the namespace that
// you delcared "Commands" in.
<MenuItem Header="_New" Command="{x:Static local:Commands.CreateNew}">
...
// Wherever you want to process the command. I am assuming you want to do it in your
// main window, but you can do it anywhere in the route between your main window and
// the menu item.
<Window.CommandBindings>
<CommandBinding Command="{x:Static local:Commands.CreateNew}"> Executed="CreateNew_Executed" />
</Window.CommandBindings>
...
// In the code behind for your main window (or whichever file you put the above XAML in)
private void CreateNew(object sender, ExecutedRoutedEventArgs e)
{
...
}
If you really just want a "New" command, you can skip creating the RoutedCommand
and InputGesture
, because that command is already created for you:
<MenuItem Header="_New" Command="New">
...
<Window.CommandBindings>
<CommandBinding Command="New" Executed="New_Executed" />
</Window.CommandBindings>
...
private void New_Executed(object sender, ExecutedRoutedEventArgs e)
{
...
}
It is quite explicit in the documentation for the property:
This property does not associate the input gesture with the menu item; it simply adds text to the menu item. The application must handle the user's input to carry out the action. For information on how to associate a command with a menu item, see Command.
One solution that doesn't involve commands and bindings is to override the owning Window's OnKeyDown
method and search a menu item that has a KeyGesture
that matches the keyboard event.
Here is the code for the Window's OnKeyDown override:
protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
// here I suppose the window's menu is named "MainMenu"
MainMenu.RaiseMenuItemClickOnKeyGesture(e);
}
And here is the utility code that matches a menu item with the keyboard event:
public static void RaiseMenuItemClickOnKeyGesture(this ItemsControl control, KeyEventArgs args) => RaiseMenuItemClickOnKeyGesture(control, args, true);
public static void RaiseMenuItemClickOnKeyGesture(this ItemsControl control, KeyEventArgs args, bool throwOnError)
{
if (args == null)
throw new ArgumentNullException(nameof(args));
if (control == null)
return;
var kgc = new KeyGestureConverter();
foreach (var item in control.Items.OfType<MenuItem>())
{
if (!string.IsNullOrWhiteSpace(item.InputGestureText))
{
KeyGesture gesture = null;
if (throwOnError)
{
gesture = kgc.ConvertFrom(item.InputGestureText) as KeyGesture;
}
else
{
try
{
gesture = kgc.ConvertFrom(item.InputGestureText) as KeyGesture;
}
catch
{
}
}
if (gesture != null && gesture.Matches(null, args))
{
item.RaiseEvent(new RoutedEventArgs(MenuItem.ClickEvent));
args.Handled = true;
return;
}
}
RaiseMenuItemClickOnKeyGesture(item, args, throwOnError);
if (args.Handled)
return;
}
}