【问题标题】:How to convert a VirtualKey to a char for non-US keyboard layouts in WinRT?如何将 VirtualKey 转换为 WinRT 中非美国键盘布局的字符?
【发布时间】:2024-01-22 22:44:01
【问题描述】:

在标准 .NET 中,存在 ToAscii/ToUnicode 和 MapVirtualKey 函数来处理此功能,但似乎尚未将等效函数或库引入 Metro/WinRT。如果 Metro/WinRT 中实际上没有公开的等效函数或库,那么这将使自定义文本输入框很难在美国以外的国家/地区推向市场。 具体示例:在我的自定义控件中,如果法语键盘用户按下 è、ù、é 或 à 键,则无法将它们翻译成正确的字符。例如,è 使用 VirtualKey.Number7 的扫描码,据我所知,没有办法知道键盘布局,也没有简单的方法可以根据当前键盘布局翻译该扫描码。 有人知道这方面的信息吗?

【问题讨论】:

  • 您不能使用WM_CHAR 的等效项吗?应该有某种事件可以为您提供已翻译的密钥。将键转换为字符是一个复杂的有状态过程(死键、IME 等),没有从键到字符的简单映射。
  • 实际上,答案似乎是否定的,至少对于 KeyDown 和 OnKeyDown 事件。 event.ToString() 输出事件的名称, event.Key.ToString() / event.VirtualKey.ToString() 输出在 VirtualKey 枚举中定义的 int - 没有给出输入的字符。令人讨厌的是,显然其他键盘布局在应用程序中与 TextBoxes 和其他控件一起使用,但是制作自定义控件时,我不能轻易吐出为非美国键盘布局输入的内容!
  • KeyDown 没有这个并不奇怪。令我惊讶的是缺少KeyPress/WM_CHAR。
  • 我认为我的解决方案不错。没有?
  • 不是。差远了。它没有解决许多非美国键盘布局,其中包括不是 A-Z 的字符,这是问题的全部重点。

标签: c# microsoft-metro windows-runtime windows-store-apps


【解决方案1】:

我想这取决于你在寻找什么。如果您正在寻找一个简单的英文字母或数字,您可以这样做:

private static char? ToChar(VirtualKey key, bool shift)
{
    // convert virtual key to char
    if (32 == (int)key)
        return ' ';

    VirtualKey search;

    // look for simple letter
    foreach (var letter in "ABCDEFGHIJKLMNOPQRSTUVWXYZ")
    {
        if (Enum.TryParse<VirtualKey>(letter.ToString(), out search) && search.Equals(key))
            return (shift) ? letter : letter.ToString().ToLower()[0];
    }

    // look for simple number
    foreach (var number in "1234567890")
    {
        if (Enum.TryParse<VirtualKey>("Number" + number.ToString(), out search) && search.Equals(key))
            return number;
    }

    // not found
    return null;
}

祝你好运!

【讨论】:

    【解决方案2】:

    对于 WinRT(带有 TextBox 且仅选择 Letters 的示例):

     CoreWindow.GetForCurrentThread().CharacterReceived += TextBox_CharacterReceived;
     void TextBox_CharacterReceived(CoreWindow sender, CharacterReceivedEventArgs args)
        {
            char c = Convert.ToChar(args.KeyCode);
            if (char.IsLetter(c))
            {
               ...
            }
        }
    

    对于取消 de char 条目:

    TextBox textBoxData;
    
    void TextBox_KeyDown(object sender, KeyRoutedEventArgs e)
        {
            textBoxData = sender as TextBox;
            if (e.Key!=VirtualKey.Space)
            e.Handled = true;
        }
    

    可选:

    void TextBox_Paste(object sender, TextControlPasteEventArgs e)
       {
           e.Handled = true;
       }
    

    并修改 CharacterReceived:

    if (char.IsLetter(c))
       {
         textBoxData.Text += c;
         textBoxData.SelectionStart = textBoxData.Text.Length;
        }
    

    警告!如果您的 TextBox 有 MaxLength,您必须修改您的代码:

    if (char.IsLetter(c) && textBoxData.Text.Length < textBoxData.MaxLength)
    

    【讨论】:

      【解决方案3】:

      您可以使用 Win32 API 函数 MapVirtualKey 将虚拟键映射到一组值,其中一个是字符值。 MapVirtualKey 似乎使用当前设置的键盘布局(未记录)。要使用指定的布局,您可以使用 MapVirtualKeyEx。

      MapVirtualKey 不考虑是否按下了 Shift。要在按下 Shift 时轻松获取信息,您可以使用 GetKeyState(不幸的是,WinRT 团队无法轻松获取修饰键的状态)。

      以下是如何将虚拟键转换为字符的示例:

      [DllImport("user32.dll")]
      private static extern uint MapVirtualKey(uint uCode, uint uMapType);
      
      [DllImport("user32.dll")]
      private static extern short GetKeyState(uint nVirtKey);
      
      private const uint MAPVK_VK_TO_CHAR = 0x02;
      
      private const uint VK_SHIFT = 0x10;
      
      private char TranslateVirtualKeyIntoChar(VirtualKey key)
      {
         char c = (char)MapVirtualKey((uint)key, MAPVK_VK_TO_CHAR);
         short shiftState = GetKeyState(VK_SHIFT);
         if (shiftState < 0)
         {
            // Shift is pressed
            c = char.ToUpper(c);
         }
         return c;
      }
      

      更新

      很遗憾,此解决方案不适用于必须经过认证的 Windows 应用商店应用。认证失败,因为使用了 MapVirtualKey 和 GetKeyState 不受支持的 API。这也意味着此解决方案很可能无法在 WinRT 下运行。

      【讨论】:

      • 您确定这些在 WinRT 中可用吗?似乎不太可能。
      • 我正要检查:不幸的是它不是。认证失败。
      最近更新 更多