6

My goal is to implement a custom Control + S key press handler to wire up to a custom save method in a winforms app.

There are several ways to accomplish this based on my R&D. First, I tried the obvious KeyPress event handler. This wasn't powerful enough to capture the key presses I need (it wasn't called on the Editor level, which is what I needed).

The second option which looks better is the protected override bool ProcessCmdKey(ref Message msg, Keys keyData) override. This works - it intercepts the CTRL key click, but apparently I need to write extra code to persist the fact the CTRL key is pressed and intercept the next key press (which would be S in my case) and then perform the custom action.

 protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
    {
        if (keyData == (Keys.S | Keys.Control))
        {
            // This is never called
        }
        else if (keyData == (Keys.Menu | Keys.Alt))
        {
           // this is called each time I hit CTRL
        }
        return true;
    }

ProcessCmdKey seems to be called immediately after I hit the CTRL key.

This post suggests creating a KeyTracker class that will persist the keys pressed and do what it needs to do:

Capture Key Sequence via ProcessCmdKey

Which seems like a good option, but before I dig in an implement a memento tracking pattern, does anyone have input on how else to accomplish this seemingly common feature?

Another pattern uses the GetKeyboardState API function:

Capture multiple key downs in C#

This seems interesting, though I'm not sure it will suite my needs.

[DllImport ("user32.dll")]

public static extern int GetKeyboardState( byte[] keystate );

private void Form1_KeyDown( object sender, KeyEventArgs e )
{
  byte[] keys = new byte[255];

  GetKeyboardState (keys);

  if( keys[(int)Keys.Up] == 129 && keys[(int)Keys.Right] == 129 )
  {
      Console.WriteLine ("Up Arrow key and Right Arrow key down.");
  }
}

Thank you for taking a look at my problem.

UPDATE

I've added three events for key handling to my DataPanel. None of these events are being picked up by VS when I set breakpoints in the events, so this is what leads me to believe that ProcessCmdKey is my best option.

If I could get these Events to work, that would be good as well:

        // Ctrl + S: Save Support
        this.ParentForm.KeyPreview = true;
        this.KeyPress             += new KeyPressEventHandler(DataPanel_KeyPress);
        this.KeyDown              += new KeyEventHandler(DataPanel_KeyDown);
        this.PreviewKeyDown       += new PreviewKeyDownEventHandler(DataPanel_PreviewKeyDown);

None of these events seem to be caught when pressing any keys:

void DataPanel_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
    {
        if (e.KeyCode == (Keys.S | Keys.Control))
        {
            SessionManager.Trace.AddTrace("You Hit Save!!");
        }
    }

    void DataPanel_KeyDown(object sender, KeyEventArgs e)
    {
        if (e.KeyCode == (Keys.S | Keys.Control))
        {
            SessionManager.Trace.AddTrace("You Hit Save!!");
        }
    }

    void DataPanel_KeyPress(object sender, KeyPressEventArgs e)
    {
        var key = e.KeyChar;
    }

UPDATE

I've solved the problem by using a simple KeyUp event and the KeyPreview flag:

 void ShipmentDataPanel_KeyUp(object sender, KeyEventArgs e)
    {
        if (e.Control && e.KeyCode == Keys.S)
        {
            MessageBox.Show("Control + S Key Hit!");
        }
    }

Thank you.

Community
  • 1
  • 1
Shawn J. Molloy
  • 2,457
  • 5
  • 41
  • 59
  • possible duplicate of [Best way to implement keyboard shortcuts in winforms?](http://stackoverflow.com/questions/400113/best-way-to-implement-keyboard-shortcuts-in-winforms) – Hans Passant Dec 10 '11 at 20:10

3 Answers3

3

Set the KeyPreview property of our form to true. The summary of this property says: "Gets or sets a value indicating whether the form will receive key events before the event is passed to the control that has focus.". Then use the KeyUp event. Unless KeyPressed it gives also information on special keys like control keys.

Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188
  • Thanks - I'll try that right now. Do you know off hand if KeyPreview will work with the ProcessCmdKey() override? – Shawn J. Molloy Dec 10 '11 at 19:40
  • By the way, I'm working on the DataPanel level, so I assume I will be adding the KeyPreview property to my ParentForm? Example: this.ParentForm.KeyPreview = true; – Shawn J. Molloy Dec 10 '11 at 19:42
  • I've added an update to my question. I added the KeyPress events you suggested, but my DataPanel does not seem to want to subscribe to them. KeyPreview doesn't seem to effect the ProcessCmdKey event either (it still gets kicked off on CTRL key press). – Shawn J. Molloy Dec 10 '11 at 19:59
  • Thanks for your help Olivier - you've helped me solve my problem. – Shawn J. Molloy Dec 10 '11 at 20:17
2

I solved this by doing,

if(e.KeyData==(Keys.S | Keys.Control))
Shree Krishna
  • 8,474
  • 6
  • 40
  • 68
0

Though this is a very old question, I still like to add my answer.
The OP writes that he could not use the ProcessCmdKey because it would fire as soon he hits the Control key and would not wait until he hits the S key also.
I don't have that problem, my code below works well and the Delete() method is only called when I first hit Ctrl and then S

protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
    bool bHandled = false;
    // switch case is the easy way, a hash or map would be better, but more work to get set up.
    switch (keyData)
    {
        case Keys.F5:
            RefreshList("", "", false); // call my refresh method
            bHandled = true;
            break;
        case Keys.S | Keys.Control: // call my delete method
            Delete(false);
            bHandled = true;
            break;
        default:
            base.ProcessCmdKey(ref msg, keyData);
            break;
    }
    return bHandled;
}

protected virtual void Delete(bool handled)
{
    if (handled == false)
    {
        MessageBox.Show("delete");
    }
}


GuidoG
  • 11,359
  • 6
  • 44
  • 79