С# Как перевести виртуальный код клавиши в char?

Я пытаюсь сопоставить виртуальный код ключа с символом.

Мой код использует ProcessCmdKey для прослушивания WM_KEYDOWN, что дает мне доступ к нажатой клавише. Например, когда я нажимаю одинарную кавычку, я получаю ключ 222, который я хочу сопоставить с keychar 39, который представляет... как вы уже догадались... одинарную кавычку.

Мой контекст разработки: - .net Framework 2.0 - UserControl размещен во многих местах

Вы знаете ответ на вопрос?


person Horas    schedule 25.11.2008    source источник
comment
MapVirtualKey() ?   -  person plinth    schedule 25.11.2008


Ответы (7)


Разве это не то, что System.Windows.Form.KeysConverter класс для?

KeysConverter kc = new KeysConverter();
string keyChar = kc.ConvertToString(keyData);
person Powerlord    schedule 26.11.2008
comment
Забыл упомянуть контекст моего вопроса: - .net Framework 2.0 - UserControl Итак, ответ на ваш ответ - нет. - person Horas; 26.11.2008
comment
Только что заметил, что я никогда не отвечал на это... KeysConverter существует в .NET 2.0. - person Powerlord; 16.10.2014
comment
Хм, но это работает только частично. В качестве примера, когда это не сработает: (new KeysConverter()).ConvertToString(Keys.OemQuestion) (Спойлер: будет возвращено OemQuestion вместо ?). Кроме того, непонятно, как обращаться с клавишей Shift. Есть рабочее решение? - person Hi-Angel; 11.06.2015
comment
@Hi-Angel Убедитесь, что вы сопоставляете правильный KeysConverter с правильным перечислением Keys. System.Windows.Forms.KeyConverter для WinForms: System.Windows.Input.KeyConverter для WPF. - person Powerlord; 11.06.2015
comment
Что касается ключей-модификаторов, перечисление Keys на самом деле состоит из флагов, поэтому D на самом деле будет Keys.Shift | Keys.D - person Powerlord; 11.06.2015
comment
Реальный ответ (по крайней мере, для меня и, я думаю, для некоторых других) находится далее ниже. - person Matthias; 22.01.2017
comment
Нет, это то же самое с Keys.ToString() - person 123iamking; 28.05.2017

Да, я использовал метод MapVirtualKey. Но я ожидал более подробной информации о том, как его использовать: какую директиву DllImport использовать, какая enum специфична для сопоставления с символами и т. д.

Мне не нравятся эти ответы, когда вы гуглите около 5 секунд, а затем просто предлагаете решение: настоящая проблема состоит в том, чтобы собрать все части воедино и не тратить свое время на тонны страниц MSDN без образцов или другие форумы по кодированию. для того, чтобы получить ваш ответ. Без обид, плинтус, но твой ответ (даже хороший) был бесполезен, так как я получил этот ответ еще до того, как разместил свой вопрос на форуме!

Итак, вот, я собираюсь опубликовать то, что искал - готовое решение C#:

1- Поместите эту директиву в свой класс:

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

2- Получите свой char следующим образом:

  protected override bool ProcessCmdKey(ref Message msg, Keys keyData)      
  {
     const int WM_KEYDOWN = 0x100;

     if (msg.Msg == WM_KEYDOWN)
     {            
        // 2 is used to translate into an unshifted character value 
        int nonVirtualKey = MapVirtualKey((uint)keyData, 2);

        char mappedChar = Convert.ToChar(nonVirtualKey);
     }

     return base.ProcessCmdKey(ref msg, keyData);
  }

Спасибо за заботу... и наслаждайтесь!

person Horas    schedule 26.11.2008
comment
Спасибо за внимание. Если вы уже рассматривали MapVirtualKey(), возможно, вам следовало включить это в свой вопрос (например, я просмотрел MapVirtualKey(), но не знаю, как вызвать его из C#). Для ваших будущих нужд вам может пригодиться pinvoke.net. - person plinth; 26.11.2008
comment
А в случае MapVirtualKey, вот запись от pinvoke.net: pinvoke.net /default.aspx/user32/MapVirtualKey.html - person plinth; 26.11.2008
comment
Незначительное исправление: MapVirtualKey на самом деле возвращает uint, а не int. Но спасибо за этот кусок кода, это было именно то, что мне было нужно для моего собственного проекта. - person Nick Spreitzer; 12.04.2010
comment
Очень полезные вопросы и ответы. Я искал это. Спасибо. - person Mojtaba Rezaeian; 22.01.2016
comment
ЭТО должен быть ответ с наибольшим количеством голосов. В настоящее время наибольшее количество голосов не используется для символов, которые не являются буквенно-цифровыми. - person Mickael Bergeron Néron; 11.10.2018

Я искал что-то подобное, но мне нужен был символ, сопоставленный с текущей раскладкой клавиатуры. Поскольку ни один из приведенных выше ответов не соответствовал моим требованиям, вот что я придумал.


        public string KeyCodeToUnicode(Keys key)
        {
            byte[] keyboardState = new byte[255];
            bool keyboardStateStatus = GetKeyboardState(keyboardState);

            if (!keyboardStateStatus)
            {
                return "";
            }

            uint virtualKeyCode = (uint)key;
            uint scanCode = MapVirtualKey(virtualKeyCode, 0);
            IntPtr inputLocaleIdentifier = GetKeyboardLayout(0);

            StringBuilder result = new StringBuilder();
            ToUnicodeEx(virtualKeyCode, scanCode, keyboardState, result, (int)5, (uint)0, inputLocaleIdentifier);

            return result.ToString();
        }

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

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

        [DllImport("user32.dll")]
        static extern IntPtr GetKeyboardLayout(uint idThread);

        [DllImport("user32.dll")]
        static extern int ToUnicodeEx(uint wVirtKey, uint wScanCode, byte[] lpKeyState, [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszBuff, int cchBuff, uint wFlags, IntPtr dwhkl);

person Ivan Petrov    schedule 05.08.2016
comment
Отлично. Он отлично работает с неанглийскими символами. Я использую его из события KeyDown и вызываю его с помощью e.Keycode или e.KeyData, и оба работают. Спасибо - person Čikić Nenad; 19.08.2016
comment
Герой, который нам нужен, но не тот герой, которого мы заслуживаем! - person Georgi-it; 15.09.2016
comment
Отлично. Работает как положено. С другой стороны, такая очевидная задача требует большого количества кода. Отличная работа. - person Wojciech Jakubas; 06.03.2018
comment
Я заметил неожиданные результаты при удерживании клавиши-модификатора при вызове функции и начал задаваться вопросом... Зачем получать текущее состояние клавиатуры? Работает, как и ожидалось, без части, связанной с текущим состоянием клавиатуры, и я не понимаю, почему текущее состояние в любом случае будет иметь значение ... я что-то упустил? - person Mikk3lRo; 25.04.2018
comment
Похоже, это было снято (без указания авторства) с PInvoke.net (pinvoke.net/default. aspx/user32.tounicodex) - person edtheprogrammerguy; 11.06.2018
comment
@edtheprogrammerguy, учитывая качественные статьи pinvoke.net/default.aspx/user32. hello%20from%20spws и pinvoke.net/default.aspx/ на pinvoke.net, вероятно, можно с уверенностью сказать, что подъем был сделан наоборот. - person Pebermynte Lars; 17.07.2019

Прочитав и протестировав некоторые из предоставленных ответов, я решил предложить альтернативу.

Как упоминалось в MM, System.Windows.KeysConverter предоставляет не символьное представление ключа, а имя перечисления, например. «Введите» вместо «\n».

Метод MapVirtualKey, предложенный Хорасом в ответ на его собственный вопрос, является хорошей отправной точкой, но по-прежнему не поддерживает ни capslock или символы, введенные с помощью клавиши Shift, например '!', '$' и '>'.

Альтернативой методу MapVirtualKey, который я использую, является метод расширения для класса Keys:

public static char ToChar(this Keys key)
{
    char c = '\0';
    if((key >= Keys.A) && (key <= Keys.Z))
    {
        c = (char)((int)'a' + (int)(key - Keys.A));
    }

    else if((key >= Keys.D0) && (key <= Keys.D9))
    {
        c = (char)((int)'0' + (int)(key - Keys.D0));
    }

    return c;
}

Показанный выше метод обеспечивает поддержку буквенно-цифровых символов. Поддержка дополнительных символов может быть реализована либо с помощью оператора switch, либо с помощью таблицы поиска.

person SongWithoutWords    schedule 27.02.2015
comment
Здесь используется System.Windows.Forms или System.Windows.Input? Key является перечислением в System.Windows.Input, но здесь не используется, и непонятно, откуда берется Keys. - person ; 08.02.2017
comment
«Ключи» являются частью «System.Windows.Forms» - person Geovani Martinez; 26.01.2019

Я только что написал улучшение Иван Петров answer, чтобы отобразить строковое представление нажатых комбинаций клавиш в WPF, см. мой код ниже:

public static string GetKeyString(Key key, ModifierKeys modifiers)
{
    string result = "";
    if (key != Key.None)
    {
        // Setup modifiers
        if (modifiers.HasFlag(ModifierKeys.Control))
            result += "Ctrl + ";
        if (modifiers.HasFlag(ModifierKeys.Alt))
            result += "Alt + ";
        if (modifiers.HasFlag(ModifierKeys.Shift))
            result += "Shift + ";
        // Get string representation
        string keyStr = key.ToString();
        int keyInt = (int)key;
        // Numeric keys are returned without the 'D'
        if (key >= Key.D0 && key <= Key.D9)
            keyStr = char.ToString((char)(key - Key.D0 + '0'));
        // Char keys are returned directly
        else if (key >= Key.A && key <= Key.Z)
            keyStr = char.ToString((char)(key - Key.A + 'A'));
        // If the key is a keypad operation (Add, Multiply, ...) or an 'Oem' key, P/Invoke
        else if ((keyInt >= 84 && keyInt <= 89) || keyInt >= 140)
            keyStr = KeyCodeToUnicode(key);
        result += keyStr;
    }
    return result;
}

private static string KeyCodeToUnicode(Key key)
{
    byte[] keyboardState = new byte[255];
    bool keyboardStateStatus = GetKeyboardState(keyboardState);

    if (!keyboardStateStatus)
    {
        return "";
    }

    uint virtualKeyCode = (uint)KeyInterop.VirtualKeyFromKey(key);
    uint scanCode = MapVirtualKey(virtualKeyCode, 0);
    IntPtr inputLocaleIdentifier = GetKeyboardLayout(0);

    StringBuilder result = new StringBuilder();
    ToUnicodeEx(virtualKeyCode, scanCode, new byte[255], result, (int)5, (uint)0, inputLocaleIdentifier);

    return result.ToString();
}

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

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

[DllImport("user32.dll")]
static extern IntPtr GetKeyboardLayout(uint idThread);

[DllImport("user32.dll")]
static extern int ToUnicodeEx(uint wVirtKey, uint wScanCode, byte[] lpKeyState, [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszBuff, int cchBuff, uint wFlags, IntPtr dwhkl);
person edupeux    schedule 12.07.2017
comment
Хм, это работает, но если нажата не буквенно-цифровая клавиша, результатом будет пустая строка. Точно так же, если пользователь нажмет space или TAB, результат будет невидимым, что заставит пользователя думать, что результат не возвращен. Есть ли где-нибудь список небуквенно-цифровых кодов клавиш и соответствующих им имен, чтобы мы могли вернуть имя нажатой клавиши? - person pookie; 30.11.2018
comment
Возможно, добавьте больше условий в метод GetKeyString. - person edupeux; 01.12.2018

KeysConverter получает имя ключа, а не ключи «текст», например: «Num2» вместо «2». MapVirtualKey будет работать для английского языка, но для документов, отличных от английского, указывается использование MapVirtualKeyEx, но для этого требуется идентификатор локали, который загружается LoadKeyBoardLayout, который требует константа идентификатора культуры, но затем, после нахождения правильных значений идентификатора, это не сработало, как я пробовал, поэтому, наконец, я сбросил все это

person mm.    schedule 24.10.2009
comment
MapVirtualKey работает и с раскладками, отличными от английского. он использует фактическую раскладку клавиатуры. - person X181; 04.11.2013

Если вы используете WPF (я не пробовал его с WinForms), есть очень простое решение, которое возвращает непосредственно сжатый символ, Событие TextInput окна.

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        TextInput += MainWindow_TextInput;
    }

    private void MainWindow_TextInput(object sender, System.Windows.Input.TextCompositionEventArgs e)
    {
        txtInput.Text += e.Text;
    }
}
person Francesco Bonizzi    schedule 13.05.2020