I need a simple way to set a shortcut for menu items.
But this don´t work with shortcut, just with click:
In my humble opinion is much easier just to use _ at the Header. This will create automatically the desired HotKey.
For example:
<MenuItem Header="_Editar">
<MenuItem Header="_Procurar" Name="MenuProcurar"
InputGestureText="Ctrl+F"
Click="MenuProcurar_Click">
<MenuItem.ToolTip>
<ToolTip>
Procurar
</ToolTip>
</MenuItem.ToolTip>
</MenuItem>
</MenuItem>
This work for me
<ContextMenu PreviewKeyUp="ContextMenu_PreviewKeyUp">
<MenuItem Header="Delete" Click="DeleteID" />
</ContextMenu>
Code behind:
private void ContextMenu_PreviewKeyUp(object sender, KeyEventArgs e)
{
ContextMenu contextMenu = sender as ContextMenu;
if (e.Key == Key.D)
{
//DELETE ID
}
contextMenu.IsOpen = false;
}
You need to use KeyBindings (and CommandBindings if you (re)use RoutedCommands such as those found in the ApplicationCommands class) for that in the controls where the shortcuts should work.
e.g.
<Window.CommandBindings>
<CommandBinding Command="New" Executed="CommandBinding_Executed" />
</Window.CommandBindings>
<Window.InputBindings>
<KeyBinding Key="N" Modifiers="Control" Command="New"/>
</Window.InputBindings>
For custom RoutedCommands
:
static class CustomCommands
{
public static RoutedCommand DoStuff = new RoutedCommand();
}
usage:
<Window
...
xmlns:local="clr-namespace:MyNamespace">
<Window.CommandBindings>
<CommandBinding Command="local:CustomCommands.DoStuff" Executed="DoStuff_Executed" />
</Window.CommandBindings>
<Window.InputBindings>
<KeyBinding Key="D" Modifiers="Control" Command="local:CustomCommands.DoStuff"/>
</Window.InputBindings>
...
</Window>
(It is often more convenient to implement the ICommand interface rather than using RoutedCommands
. You can have a constructor which takes delegates for Execute
and CanExecute
to easily create commands which do different things, such implementations are often called DelegateCommand
or RelayCommand
. This way you do not need CommandBindings
.)
I'm overly biased by Windows.Forms & gulp VB 6, so I kind of agree with Jonathan and Jase that there's got to be a more straightforward/procedural method to statically wire up event handlers that aren't necessarily CommandBindings
. And there is, I think.
A good tutorial for using non-CommandBinding
handlers like this, but with an emphasis on buttons, can be found in this MSDN blog post, I believe. I'll distill and target MenuItem
s...
First, create a class implementing ICommand
. You can put this anywhere, of course, even in your MainWindow.xaml.cs file if you wanted, to keep your demo code insanely simple. You'll probably want to make CanExecute
more complicated when you want to dis/en/able menu items later, but for now, we'll just always have our menu items enabled.
public class HelloWorldCommand : ICommand
{
public void Execute(object parameter)
{
MessageBox.Show(@"""Hello, world!"" from "
+ (parameter ?? "somewhere secret").ToString());
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
}
As the tutorial helpfully points out, you could call this command from anywhere already, with code like...
var hwc = new HelloWorldCommand();
if (hwc.CanExecute(this))
hwc.Execute(this);
So let's add a sort of "declaration" for the HelloWorldCommand
to our Window so that we can use it later. Inside of your Window
tags, register the command as a resource:
<Window.Resources>
<local:HelloWorldCommand x:Key="hwc"/>
</Window.Resources>
Now we have a neat shortcut for linking to this "locally namespaced" command, "hwc"
, though you can obviously use any string you want. We'll use that a lot in our xaml.
Let's add our MenuItem
s to our xaml. I've replaced the stock Grid
with a DockPanel
because that's the easiest way (for me) to have equi-spaced widgets that fill the Window
, though I've left all of the rest of my UI out.
Note the Command="{StaticResource hwc}"
s sprinkled into each MenuItem
declaration. The key is the hwc
in there - remember that that's our shortcut for the HelloWorldCommand
that we set up at the Window
level. And, of course, StaticResource
says just to look it up the Window
's resources. We're not binding anything; we're just using our shortcut.
<DockPanel LastChildFill="True">
<Menu DockPanel.Dock="Top">
<MenuItem Header="_File">
<MenuItem
Header="_Open"
Command="{StaticResource hwc}"
>
<MenuItem.CommandParameter>
<!-- so you could make this object as complex as you wanted,
like, say, your entire Window. See magic incantation, below. -->
<Binding RelativeSource="{RelativeSource FindAncestor, AncestorType=Window}" />
</MenuItem.CommandParameter>
</MenuItem>
<MenuItem
Header="_Close"
Command="{StaticResource hwc}"
CommandParameter="Close"
InputGestureText="Ctrl+G" />
<MenuItem
Header="_Save"
Command="{StaticResource hwc}"
CommandParameter="Save" />
<Separator />
<MenuItem
Header="_Quit"
Command="{StaticResource hwc}"
CommandParameter="Quit" />
</MenuItem>
</DockPanel>
Note that we're using the same Command for everything! But how can we tell which widget threw the event? For that, you need to use the CommandParameter
-- remember our Execute
method's signature: Execute(object parameter)
. That CommandParameter
parameter is what we can use to know how to handle the event. Try running this and note that the MessageBox
will use whatever's in CommandParameter
to let you know the source of the event. We're doing it all manually, but that's not too bad.
Also note that you can make these objects as complicated as you'd like. You can use a property in the MenuItem
tag to define the parameter, or can use "real" <MenuItem.CommandParameter>
tags, like in the Open menu item, above, to define something complex. In this case, we're passing the entire parent Window
object, which was the easiest (though not the cleanest) way to throw our VB6-ish context into the event handler code.
MenuItem
s (aka, "Answering the OP")And now we can finally answer the original question! Let's finally wire up a keyboard shortcut for the Close
menu item. You'll note that we've already declared an InputGestureText
. By itself, InputGestureText
is only cosmetic. If we were overly picky, we could argue the mechanism for creating the keyboard shortcut doesn't have any direct, inherent relationship with the MenuItem
at all!
We need to instead (or additionally) register a listener for Ctrl-G at the Window
level to catch the keystroke. So at the top level of your Window tags, insert this (taken essentially from here):
<Window.InputBindings>
<KeyBinding Modifiers="Control"
Key="G"
Command="{StaticResource hwc}"
CommandParameter="window input binding"
/>
</Window.InputBindings>
Note that you could put CommandParameter
tags in your KeyBinding
by moving it from a self-closing piece of XML to "real" open and close KeyBinding
tags.
And we're done. Run your app, and press Ctrl-G. Whaddup.
Pretty straightforward, once you've got the players straight, and much less magic binding-y than most intros to commands and MenuItems
, I think.
Possible pro tip:
The whole
CommandBinding
thing confused me for a while. This is just for specific command types, I believe. That is, you can't just wire up anyCommand
you like. Stuff like what's bragged about here (in what's admittedly a decent intro tutorial!)...It might not be completely obvious, but by using commands, we just got a whole bunch of things for free: Keyboard shortcuts, text and InputGestureText on the items and WPF automatically enables/disables the items depending on the active control and its state. In this case, Cut and Copy are disabled because no text is selected, but Paste is enabled, because my clipboard is not empty!
... is kind of magic-y and not necessarily good, and can be confusing when you're new to WPF menus.
H.B. was right... I just wanted to add more precisions.
Remove the Click
event on your MenuItem
and associate it with a Command
instead.
1 - Add/create your commands:
<Window.CommandBindings>
<CommandBinding Command="Open" Executed="OpenCommandBinding_Executed"/>
<CommandBinding Command="SaveAs" Executed="SaveAsCommandBinding_Executed"/>
</Window.CommandBindings>
The commands are refering to the following code:
private void OpenCommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
{
Open();//Implementation of open file
}
private void SaveAsCommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
{
SaveAs();//Implementation of saveAs
}
2 - Associate the commands with the wanted keys:
<Window.InputBindings>
<KeyBinding Key="O" Modifiers="Control" Command="Open"/>
<KeyBinding Key="S" Modifiers="Control" Command="SaveAs"/>
</Window.InputBindings>
3 - Finally assign the commands with you menu item (InputGestureText
is just a decorating text):
<Menu Name="menu1">
<MenuItem Header="_File">
<MenuItem Name="menuOpen" Header="_Open..." Command="Open" InputGestureText="Ctrl+O"/>
<MenuItem Name="menuSaveAs" Header="_Save as..." Command="SaveAs" InputGestureText="Ctrl+S"/>
</MenuItem>
</Menu>
That way multiple inputs may be associated to the same command.
You can also declare RoutedUICommand
in XAML:
<Window.Resources>
<RoutedUICommand x:Key="BuildCmd" Text="Build">
<RoutedUICommand.InputGestures>
<KeyGesture>CTRL+SHIFT+B</KeyGesture>
</RoutedUICommand.InputGestures>
</RoutedUICommand>
</Window.Resources>
Do the binding
<Window.CommandBindings>
<CommandBinding Command="{StaticResource BuildCmd}" Executed="BuildCmdExecuted"/>
</Window.CommandBindings>
And in MenuItem
<MenuItem Command="{StaticResource BuildCmd}"/>
Another solution is discussed here.