34

My WPF application handles keyboard presses and specifically the # and * character as it is a VoIP phone.

I have a bug though with international keyboards, and in particular the British english keyboard. Normally I listen for the 3 key and if the shift key modifier is down we fire off an event to do stuff. However on the British keyboard this is the '£' character. I found that the UK english keyboard has a dedicated key for '#'. Obviously we could just listen for that particular key, but that doesn't solve the case for US english which is shift-3 and all the countless other keyboards that put it somewhere else.

Long story short, how do I listen for a particular character from a key press, whether it's a key combo or single key and react to it?

Makoto
  • 104,088
  • 27
  • 192
  • 230
Jippers
  • 2,635
  • 5
  • 37
  • 58

2 Answers2

66

The function below, GetCharFromKey(Key key) will do the trick.

It uses a series of win32 calls to decode the key pressed:

  1. get the virtual key from WPF key

  2. get the scan code from the virtual key

  3. get your unicode character

This old post describes it in a bit more detail.

      public enum MapType : uint
      {
         MAPVK_VK_TO_VSC = 0x0,
         MAPVK_VSC_TO_VK = 0x1,
         MAPVK_VK_TO_CHAR = 0x2,
         MAPVK_VSC_TO_VK_EX = 0x3,
      }

      [DllImport("user32.dll")]
      public static extern int ToUnicode(
          uint wVirtKey,
          uint wScanCode,
          byte[] lpKeyState,
          [Out, MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 4)] 
            StringBuilder pwszBuff,
          int cchBuff,
          uint wFlags);

      [DllImport("user32.dll")]
      public static extern bool GetKeyboardState(byte[] lpKeyState);

      [DllImport("user32.dll")]
      public static extern uint MapVirtualKey(uint uCode, MapType uMapType);

      public static char GetCharFromKey(Key key)
      {
         char ch = ' ';

         int virtualKey = KeyInterop.VirtualKeyFromKey(key);
         byte[] keyboardState = new byte[256];
         GetKeyboardState(keyboardState);

         uint scanCode = MapVirtualKey((uint)virtualKey, MapType.MAPVK_VK_TO_VSC);
         StringBuilder stringBuilder = new StringBuilder(2);

         int result = ToUnicode((uint)virtualKey, scanCode, keyboardState, stringBuilder, stringBuilder.Capacity, 0);
         switch (result)
         {
            case -1: 
               break;
            case 0: 
               break;
            case 1:
               {
                  ch = stringBuilder[0];
                  break;
               }
            default:
               {
                  ch = stringBuilder[0];
                  break;
               }
         }
         return ch;
      }
Yoh Deadfall
  • 2,711
  • 7
  • 28
  • 32
George
  • 1,508
  • 16
  • 13
  • 1
    I've been using this for some time now and noticed a bug. Two keys returning the same result from GetCharFromKey. To verify, simply subclass a TextBox, override PreiewKeyDown, strike the spacebar, (which is 32 char), then strike the Delete key, also returns 32 char. Is anyone else seeing this? – gcadmes Jun 04 '13 at 13:40
  • 1
    Well that was humbling... The first statement in the GetCharFromKey is the char ch = ' ';. So for cases falling into '0' or '1', the return char was always a space (char)32. (forehead slap) – gcadmes Jun 04 '13 at 15:48
  • 4
    It really should either return `'\0'` for keys that do not produce a text character, or `(char?)null`. – jnm2 May 19 '14 at 13:37
  • @George what about silverlight? – rr- Mar 27 '15 at 15:16
  • 2
    The "old post" mentioned is now gone, but can still be accessed here: http://web.archive.org/web/20111229040043/http://huddledmasses.org/how-to-get-the-character-and-virtualkey-from-a-wpf-keydown-event. – Olly Apr 18 '16 at 15:08
  • 2
    For the record, WinForms version is [here](http://stackoverflow.com/a/38787314/568266) – Matthias Jan 22 '17 at 02:00
  • 1
    It needs a reference to assembly WindowsBase and then using System.Windows.Input; – qwlice Sep 27 '18 at 11:20
  • 2
    Has anyone put this in a NuGet package? – Jared Beach Nov 12 '18 at 16:48
  • I found that the call to "ToUnicode" function changes something with the keyboard state. . . . This causes problems when trying to interpret letters with accents, umlauts and so on (like üöéâ and more). . . . Calling it like below resolves the issue for me . . . : int result = ToUnicode((uint)virtualKey, scanCode, keyboardState, stringBuilder, stringBuilder.Capacity, 4); – kesse Feb 22 '23 at 16:51
4

I found a helpful solution in this post: http://www.codewrecks.com/blog/index.php/2008/05/06/wpf-convert-systemwindowsinputkey-to-char-or-string-2/

There is another event TextInput or PreviewTextInput which gives the character as string instead of the Key :).

JanDotNet
  • 3,746
  • 21
  • 30