开发者

Can I create a KeyBinding for a sequence of keys in WPF?

Is it possible to define key bindings in WPF for a sequence of key presses like the shortcuts in Visual Studio e.g. Ctrl + R, Ctrl + A is run all tests in current solution

As far as I can see I can only bind single key combinations like Ctrl + S using the element. Can I bind sequences using this or will I have to manually handle th开发者_运维技巧e key presses to do this?


You need to create your own InputGesture, by overriding the Matches method.

Something like that:

public class MultiInputGesture : InputGesture
{
    public MultiInputGesture()
    {
        Gestures = new InputGestureCollection();
    }

    public InputGestureCollection Gestures { get; private set; }

    private int _currentMatchIndex = 0;

    public override bool Matches(object targetElement, InputEventArgs inputEventArgs)
    {
        if (_currentMatchIndex < Gestures.Count)
        {
            if (Gestures[_currentMatchIndex].Matches(targetElement, inputEventArgs))
            {
                _currentMatchIndex++;
                return (_currentMatchIndex == Gestures.Count);
            }
        }
        _currentMatchIndex = 0;
        return false;
    }
}

It probably needs a little more than that, like ignoring certain events (e.g. KeyUp events between KeyDown events shouldn't reset _currentMatchIndex), but you get the picture...


The answer by @ThomasLevesque is mostly correct, but doesn't deal with repeating keys. (Note that holding the Ctrl key down causes key-repeat events to be generated.) It can also be useful to time out if the user stalls mid-sequence. Here's what I'm using:

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'm using this by defining the RoutedUICommand in XAML:

<Window.Resources>
    <RoutedUICommand x:Key="MyCommand" Text="My Command"/>
</Window.Resources>

This 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 MenuItem, unless you want to try the DisplayString hack in the linked forum post.

NOTE: the key gesture handler "eats" the 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 (e.g. Ctrl+H, Ctrl+C followed immediately by Ctrl+H, Ctrl+D), the second handler won't reset when Ctrl+C is hit. Instead, it'll reset when the second Ctrl+H arrives, and will miss the combo. The actual behavior is dependent upon the order in which the handlers are called. I'm currently handling this by defining a static event that fires when a match is found, and subscribing all instances to it.

Update: one other thing to be aware of: the order of items in <Window.CommandBindings> matters. If you have a Copy handler that fires on Ctrl+C, it must appear in the list after the multi-key gesture for Ctrl+H, Ctrl+C.


   <KeyBinding x:Name="mykeybinding" Gesture="CTRL+P" Key="E" 
                 Command="mycommand"/>

That seems to do trick at my end, I have have to press ctrl+P+E to execute "mycommand"

Based on http://msdn.microsoft.com/en-in/library/system.windows.input.keybinding.aspx

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜