how to capture the '#' character on different locale keyboards in WPF/C#?
Asked Answered
N

3

35

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?

Newsmagazine answered 28/4, 2011 at 22:34 Comment(0)
C
67

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;
      }
Conglobate answered 28/4, 2011 at 23:19 Comment(9)
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?Housecoat
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)Housecoat
It really should either return '\0' for keys that do not produce a text character, or (char?)null.Sexagenary
@Conglobate what about silverlight?Importation
The "old post" mentioned is now gone, but can still be accessed here: web.archive.org/web/20111229040043/http://huddledmasses.org/….Placeman
For the record, WinForms version is hereSnuck
It needs a reference to assembly WindowsBase and then using System.Windows.Input;Tevet
Has anyone put this in a NuGet package?Deceleron
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);Rommel
C
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 :).

Cressler answered 24/9, 2019 at 20:22 Comment(0)
B
0

Based on the input from the answer of @JanDotNet where the link doesn't lead to an existent website anymore: it worked for me using the PreviewTextInput event.

Xaml:

<TextBox x:Name="NameTextBox" ... PreviewTextInput="NameTextBox_PreviewTextInput"/>

Code behind:

private void NameTextBox_PreviewTextInput(object sender, System.Windows.Input.TextCompositionEventArgs e)
{
    // Check for forbidden characters (as negatives of the allowed characters) in the input.
    if (Regex.IsMatch(e.Text, @"[^0-9^A-z^\+^\-^\=^\<^\>^\%^\[^\]]"))
    {
        // Prevent the character being entered into the text box.
        e.Handled = true;
    }
}
Blancablanch answered 30/11, 2023 at 8:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.