Reynevan
Reynevan

Reputation: 1545

How can I handle a customizable hotkey setting?

I'm trying to let my app's users an option to set a keyboard hotkey for for some feature.

Right now I'm using a TextBox with the KeyDown event as follows:

Key Hotkey;
private void SetHotKey(object sender, KeyEventArgs e)
{
    (sender as TextBox).Text = e.Key.ToString();
    Hotkey = e.Key;
    e.Handled = true;
}

The problem with this approach is I can't set complex shortcuts, like [Ctrl]+[F4]. Is there some 3rd-party control that helps with that? Or a better-suited event to subscribe to on a textbox?

UPDATE: I've changed my code but it seems I'm still doing something wrong.

Key Hotkey;
bool lControl = false;
bool lAlt = false;
bool lShift = false;
private void SetHotKey(object sender, KeyEventArgs e)
{
    var k = e.Key;
    if (e.IsDown)
    {
        var tb = sender as TextBox;
        tb.Text = "";
        lControl = Keyboard.IsKeyDown(Key.LeftCtrl);
        lAlt = Keyboard.IsKeyDown(Key.LeftAlt);
        lShift = Keyboard.IsKeyDown(Key.LeftShift);
        if (lControl) tb.Text += "Ctrl+";
        if (lAlt) tb.Text += "Alt+";
        if (lShift) tb.Text += "Shift+";
        tb.Text = e.Key.ToString();
        Hotkey = e.Key;
    }
    e.Handled = true;
}

How can I make it work and look cleaner as well?

Upvotes: 2

Views: 1668

Answers (2)

Janne Matikainen
Janne Matikainen

Reputation: 5121

To get you started in the right direction.

First you will need the real key behind the System/ImeProcessed/DeadCharProcessed key. This can be done with extension method for easier access.

public static Key RealKey(this KeyEventArgs e)
{
    switch (e.Key)
    {
        case Key.System:
            return e.SystemKey;

        case Key.ImeProcessed:
            return e.ImeProcessedKey;

        case Key.DeadCharProcessed:
            return e.DeadCharProcessedKey;

        default:
            return e.Key;
    }
}

And then you should format your modifiers to the shortcut, not just the Key which was pressed. You can use Keyboard.ModifierKeys to get flags and for easier formatting, gather those in a list. And also you should block just a modifier key (Ctrl, Alt and Shift) from updating the hotkey.

private void SetHotKey(object sender, KeyEventArgs e)
{
    var nonShortcuttableKeys = new[] { Key.LeftAlt, Key.RightAlt, Key.LeftCtrl, Key.RightCtrl, Key.LeftShift, Key.RightShift };
    var actualKey = e.RealKey();

    if (e.IsDown && !nonShortcuttableKeys.Contains(actualKey))
    {
        var tb = sender as TextBox;

        var modifiers = new List<ModifierKeys>();
        if (Keyboard.Modifiers.HasFlag(ModifierKeys.Control))
        {
            modifiers.Add(ModifierKeys.Control);
        }

        if (Keyboard.Modifiers.HasFlag(ModifierKeys.Alt))
        {
            modifiers.Add(ModifierKeys.Alt);
        }

        if (Keyboard.Modifiers.HasFlag(ModifierKeys.Shift))
        {
            modifiers.Add(ModifierKeys.Shift);
        }

        tb.Text = modifiers.Count == 0
            ? string.Format("{0}", actualKey)
            : string.Format("{0} + {1}", string.Join(" + ", modifiers), actualKey);

        Hotkey = actualKey;
    }

    e.Handled = true;
}

Upvotes: 8

Mat
Mat

Reputation: 2072

WinForms You can check the KeyEventArgs.Modifiers if the Ctrl is pressed or not. https://msdn.microsoft.com/en-us/library/system.windows.forms.keyeventargs.modifiers(v=vs.110).aspx

WPF

you can use this little snippet to check if the Control key is pressed:

Keyboard.IsKeyDown(Key.LeftCtrl)

If you want to use the MVVM pattern you can use the KeyBindings of the textbox (or any other control) Create Key binding in WPF

Upvotes: 0

Related Questions