16

I want to write a function like so,

        public System.Windows.Input.Key ResolveKey(char charToResolve)
        {
            // Code goes here, that resolves the charToResolve
            // in to the Key enumerated value
            // (For example with '.' as the character for Key.OemPeriod)

        }

I know I can write a huge Switch-case to match the character, but is there any other way? The thing with this is the Key enum's string may not match with the character so Enum.IsDefined will not work

Any ideas?

Update: This is in Windows environment

Vin
  • 6,115
  • 4
  • 41
  • 55

4 Answers4

27
[DllImport("user32.dll")]
static extern short VkKeyScan(char ch);

static public Key ResolveKey(char charToResolve)
{
    return KeyInterop.KeyFromVirtualKey(VkKeyScan(charToResolve));
}
xcud
  • 14,422
  • 3
  • 33
  • 29
  • 1
    `VkKeyScan()` uses the high byte to encode modifier key states so this fails for capital letters because `VkKeyScan()` adds in the shift flag. If you and the return value of `VkKeyScan()` with `0xFF` to clear those flags it will work with capital letters. – shf301 Dec 19 '14 at 16:30
  • which namespace/class needs to set `using` ? probably reference to `WindowsBase.dll` is needed and using `System.Windows.Input`, right? – T.Todua Feb 15 '19 at 17:39
7

Try using the ConvertFrom method of the System.Windows.Input.KeyConverter class.

Joacim Andersson
  • 381
  • 3
  • 11
  • This isn't meant to be used that way. Take a peek at it in Reflector and you'll see it only supports letters, digits, and a few other keys. Notable exceptions include ?, which would need to be the string "OemQuestion" to get converted properly. – OwenP Dec 02 '09 at 22:34
1

Recently I found a great answer for similar question from Jon Hanna which can handle control key states as well:

This one might be more easily explained with an example program than anything else:

namespace KeyFinder
{
  class Program
  {
    [DllImport("user32.dll", CharSet = CharSet.Unicode)]
    static extern short VkKeyScanEx(char ch, IntPtr dwhkl);
    [DllImport("user32.dll")]
    static extern bool UnloadKeyboardLayout(IntPtr hkl);
    [DllImport("user32.dll")]
    static extern IntPtr LoadKeyboardLayout(string pwszKLID, uint Flags);
    public class KeyboardPointer : IDisposable
    {
      private readonly IntPtr pointer;
      public KeyboardPointer(int klid)
      {
        pointer = LoadKeyboardLayout(klid.ToString("X8"), 1);
      }
      public KeyboardPointer(CultureInfo culture)
        :this(culture.KeyboardLayoutId){}
      public void Dispose()
      {
        UnloadKeyboardLayout(pointer);
        GC.SuppressFinalize(this);
      }
      ~KeyboardPointer()
      {
        UnloadKeyboardLayout(pointer);
      }
      // Converting to System.Windows.Forms.Key here, but
      // some other enumerations for similar tasks have the same
      // one-to-one mapping to the underlying Windows API values
      public bool GetKey(char character, out Keys key)
      {
        short keyNumber = VkKeyScanEx(character, pointer);
        if(keyNumber == -1)
        {
          key = System.Windows.Forms.Keys.None;
          return false;
        }
        key = (System.Windows.Forms.Keys)(((keyNumber & 0xFF00) << 8) | (keyNumber & 0xFF));
        return true;
      }
    }
    private static string DescribeKey(Keys key)
    {
      StringBuilder desc = new StringBuilder();
      if((key & Keys.Shift) != Keys.None)
        desc.Append("Shift: ");
      if((key & Keys.Control) != Keys.None)
        desc.Append("Control: ");
      if((key & Keys.Alt) != Keys.None)
        desc.Append("Alt: ");
      return desc.Append(key & Keys.KeyCode).ToString();
    }
    public static void Main(string[] args)
    {
      string testChars = "Aéש";
      Keys key;
      foreach(var culture in (new string[]{"he-IL", "en-US", "en-IE"}).Select(code => CultureInfo.GetCultureInfo(code)))
      {
        Console.WriteLine(culture.Name);
        using(var keyboard = new KeyboardPointer(culture))
          foreach(char test in testChars)
          {
            Console.Write(test);
            Console.Write('\t');
            if(keyboard.GetKey(test, out key))
              Console.WriteLine(DescribeKey(key));
            else
              Console.WriteLine("No Key");
          }
      }
      Console.Read();//Stop window closing
    }
  }
}

Output:

he-IL
A  Shift: A
é  No Key
ש  A
en-US
A  Shift: A
é  No Key
ש  No Key
en-IE
A  Shift: A
é  Control: Alt: E
ש  No Key

(Though your own console might mess up ש and/or é depending on settings and fonts).

Read full descriptions from referenced answer

Community
  • 1
  • 1
Mojtaba Rezaeian
  • 8,268
  • 8
  • 31
  • 54
1

Just convert it this way:

Dim KeyConverter As New Forms.KeysConverter    
Dim S As String = KeyConverter.ConvertToString(e.Key)
Dim O As System.Windows.Forms.Keys = KeyConverter.ConvertFrom(S)
Dim ChValue As Integer = CType(O, Integer) 

In my case, I press "ENTER" on my keyboard, O is going into ENTER {13} and ChValue is going into Character Code 13 For TAB key, I will receive Character Code 9 that way, for example.

Pang
  • 9,564
  • 146
  • 81
  • 122
Nasenbaer
  • 4,810
  • 11
  • 53
  • 86
  • The problem with ALL these answers is that it won't work with DotNetCore (DotNet5,6,or7) on a machine other than a Windows machine. Surely there is a DotNetCore solution for doing this? – MC9000 Jan 29 '23 at 16:34