Search code examples
c#winformskeykeydownevent-capturing

C# - How to capture Shift+Del combination event without capturing Shift key only?


I have a textbox and a listbox. Shift must clear the listbox and Shift+Del combination must clear the textbox.

This is my code.

private void MainForm_KeyDown(object sender, KeyEventArgs e)
{
    if (e.Shift && e.KeyCode == Keys.Delete) textBox.Clear();
    if (e.Shift) listBox.Items.Clear();
}

It obviously works wrong because the listbox is always cleared when Shift is down. I need to be able to clear the textbox by pressing Shift+Del and not clear the listbox at the time.

I have to use only Shift and Del keys and no others.

So what do I have to do?


Solution

  • First, let me preface this by saying that I would normally advise against using the Shift key alone to trigger an action in any desktop software other than perhaps a game. Doing so violates the Principle of Least Astonishment: since Shift is used routinely in the course of normal typing to modify the meaning of other keys, the user is conditioned to expect that pressing it will not do anything of consequence by itself, and certainly nothing destructive. So if the user casually presses Shift and finds that he has just lost his list of items, he may be quite unpleasantly surprised, particularly if it turns out to be difficult or tedious to undo the damage.

    However, you said in the comments that you had no choice in the matter, so with that said, here is a possible solution.

    In order to be able to tell the difference between Shift by itself and a Shift + Del combination, we need to capture both the KeyDown and KeyUp events and use the information gleaned from each.

    In the KeyDown event, we save the value of the KeyEventArgs.KeyData to a private field, but don't take any action on it yet. KeyData contains the combined Keys flags representing the primary key that was pressed down, along with any modifier keys like Ctrl, Shift or Alt that were held down at the same time. Note that when Shift is pressed by itself, it is both the primary key and a modifier, so the KeyData will contain Keys.ShiftKey (the key) combined with Keys.Shift (the modifier).

    private Keys LastKeysDown;
    
    private void MainForm_KeyDown(object sender, KeyEventArgs e)
    {
        LastKeysDown = e.KeyData;
    }
    

    In the KeyUp event, we look at what keys were released relative to what was last pressed. We are looking for two specific sequences of keys:

    1. Shift + Del is pressed, then Del is released while Shift is still held down.
    2. Shift is pressed with no other key, then released.

    If we find what we're looking for, we initiate the appropriate action, otherwise we do nothing.

    private void MainForm_KeyUp(object sender, KeyEventArgs e)
    {
        // Shift + Delete pressed, then Delete released while Shift still held down
        if (LastKeysDown == (Keys.Shift | Keys.Delete) && e.KeyData == LastKeysDown)
            textBox1.Clear();
    
        // Shift pressed with no other key, then released
        else if (LastKeysDown == (Keys.Shift | Keys.ShiftKey) && e.KeyData == Keys.ShiftKey)
            listBox1.Items.Clear();
    
        LastKeysDown = Keys.None;
    }
    

    Notice that, as a last order of business, we clear the private LastKeysDown field. This helps to ensure that if we get two KeyUp events in a row, only the first one is paired with the previous KeyDown. But note this is not foolproof: if you hold a key long enough, the computer's key repeat will kick in and generate additional KeyDown events.