Translating System.Windows.Forms.Keys to char data in ProcessCmdKey

ProcessCmdKey(ref System.Windows.Forms.Message msg, System.Windows.Forms.Keys keyData) provides more powerful features than KeyDown, KeyPress, and KeyUp.  ProcessCmdKey can look at certain keys that are not received by the KeyDown et al. events (in the application where I encountered this problem, a DataGridView hosts a derived ComboBox and the DataGridView prevents certain keys from reaching the KeyPress event of the derived ComboBox), and ProcessCmdKey occurs before any of the Key events.

This means that you don’t have access to e.KeyChar from the KeyEventArgs parameter of the Key events.  To further complicate matters, the DataGridView does not pass on the WM_CHAR message to the derived ComboBox, so you never have the translated value of the message (i.e., you get the message representing the key that has been pressed but never the key character).  The upshot is that you can never trust (char)keyData or (char)msg.WParam.ToInt32() to be accurate; you have to look at the value of the key and modifiers to understand the meaning.

Another complication is that keys that represent the same character have different literal values.  The numeral 4 is actually Keys.D4 if you type the one at the top of your keyboard and Keys.NumPad4 if you use the number pad, and the conversion of the integer values to characters is different between them.

I wrote a simple function to translate from the Keys enumeration to a char:

public static char GetCharFromKeys(Keys keyData)
  char KeyValue;
  switch (keyData)
    case Keys.Add:
    case Keys.Oemplus:
      KeyValue = '+';
    case Keys.OemMinus:
    case Keys.Subtract:
      KeyValue = '-';
    case Keys.OemQuestion | Keys.Shift:
      KeyValue = '?';
    case Keys.OemQuestion:
    case Keys.Divide:
      KeyValue = '/';
      if ((0x60 <= (int)keyData) && (0x69 >= (int)keyData))
        KeyValue = (char)((int)keyData - 0x30);
        KeyValue = (char)keyData;
  return KeyValue;

There are several problems with this approach.  Obviously, this function is incomplete–I mapped the keys that I need for this particular implementation.  I’m assuming a US keyboard layout.  I’m probably relying on either ASCII values or that the character’s Unicode mapping is the same as the key value.  I’m not carefully checking Num Lock, Caps Lock, and Shift status.

Question being this: there obviously has to be a better way to do this.  Any suggestions?  My kludge works for the intended application, but it’s not the right way to do it.

For a full writeup of the intricacies of dealing with keyboards, check out Michael Kaplan’s 10-part series “Getting all you can out of a keyboard layout.”  One day I will study this, understand it, and be a better programmer for it…

3 thoughts on “Translating System.Windows.Forms.Keys to char data in ProcessCmdKey

  1. [DllImportAttribute(“User32.dll”)]
    public static extern int ToAscii(int uVirtKey,
    int uScanCode,
    byte[] lpbKeyState,
    byte[] lpChar,
    int uFlags);

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

    public static char GetAsciiCharacter(int uVirtKey, int uScanCode)
    byte[] lpKeyState = new byte[256];
    byte[] lpChar = new byte[2];
    if(ToAscii(uVirtKey, uScanCode, lpKeyState, lpChar, 0) == 1)
    return (char)lpChar[0];
    return new char();

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s