@ThomasLevesque's answer is mostly correct, but not for duplicate keys. (Note that pressing the Ctrl key will generate key repetition events.) It can also be useful for timeout if the user stops the middle sequence. Here is what I use:
public class MultiKeyInputGesture : InputGesture { private const int MAX_PAUSE_MILLIS = 1500; private InputGestureCollection mGestures = new InputGestureCollection(); private DateTime mLastWhen = DateTime.Now; private int mCheckIdx; public MultiKeyInputGesture(KeyGesture[] keys) { Debug.Assert(keys.Length > 0); // Grab a copy of the array contents. foreach (KeyGesture kg in keys) { mGestures.Add(kg); } } public override bool Matches(object targetElement, InputEventArgs inputEventArgs) { if (!(inputEventArgs is KeyEventArgs)) { // does this actually happen? return false; } DateTime now = DateTime.Now; if ((now - mLastWhen).TotalMilliseconds > MAX_PAUSE_MILLIS) { mCheckIdx = 0; } mLastWhen = now; if (((KeyEventArgs)inputEventArgs).IsRepeat) { // ignore key-repeat noise (especially from modifiers) return false; } if (!mGestures[mCheckIdx].Matches(null, inputEventArgs)) { mCheckIdx = 0; return false; } mCheckIdx++; if (mCheckIdx == mGestures.Count) { mCheckIdx = 0; inputEventArgs.Handled = true; return true; } return false; } }
I use this by defining RoutedUICommand in XAML:
<Window.Resources> <RoutedUICommand x:Key="MyCommand" Text="My Command"/> </Window.Resources>
It is referenced from <Window.CommandBindings> and <MenuItem> as usual. Then in the window constructor I do:
RoutedUICommand ruic = (RoutedUICommand)FindResource("MyCommand"); ruic.InputGestures.Add( new MultiKeyInputGesture(new KeyGesture[] { new KeyGesture(Key.H, ModifierKeys.Control, "Ctrl+H"), new KeyGesture(Key.C, ModifierKeys.Control, "Ctrl+C") }) );
I found this forum post helpful.
You will need to add an explicit InputGestureText to any InputGestureText MenuItem if you don't want to try hacking a DisplayString in a related forum post.
NOTE: the key handler eats a key that completes the gesture. If you have more than one handler, and the user tries to use two multi-key sequences in a row (for example, Ctrl + H, Ctrl + C, immediately followed by Ctrl + H, Ctrl + D), the second handler will not be reset when Ctrl + C is pressed . Instead, it will be reset when the second Ctrl + H arrives and skips the combo. Actual behavior depends on the order in which the handlers are called. I am currently doing this by defining a static event that fires when a match is found, and signing all instances on it.
Update: another thing to be aware of: the order of the elements in <Window.CommandBindings> matters. If you have a copy handler that starts with Ctrl + C, it should appear in the list after a gesture with several keys for Ctrl + H, Ctrl + C.