Как определить нажатую клавишу?

123

В Windows Forms вы можете в любой момент узнать текущую позицию курсора благодаря классу Cursors .

То же самое, кажется, недоступно для клавиатуры. Можно ли узнать, например, Shiftнажата ли клавиша?

Обязательно ли отслеживать каждое уведомление с клавиатуры (события KeyDown и KeyUp)?

Питер Мортенсен
источник
Вы работаете в среде WPF или где-то еще?
epotter 08
70
@epotter: второе слово указывает WinForms.
Уилл Эддинс,

Ответы:

162
if ((Control.ModifierKeys & Keys.Shift) != 0) 

Это также будет верно, если Ctrl+ Shiftне работает. Если вы хотите проверить, нажата ли только Shift,

if (Control.ModifierKeys == Keys.Shift)

Если вы принадлежите к наследующему классу Control(например, к форме), вы можете удалитьControl.

SLaks
источник
8
Если я чего-то не упустил, значит, вы не ответили на вопрос должным образом. OP спрашивает обо всех клавишах и использовал клавишу Shift только в качестве примера. Итак, как вы обнаружите другие ключи, такие как от A до Z, от 0 до 9 и т. Д.
Эш
2
Учитывая, что он принял ответ, похоже, что ему нужны были только ключи-модификаторы. Если вам нужны другие ключи, вам нужно вызвать GetKeyStateфункцию API.
SLaks
2
нет необходимости в GetKeyState. Вам просто нужно добавить фильтр сообщений. Смотрите мой ответ.
Ash
3
В качестве решения WPF вы можете использовать Keyboard.Modifiers == ModifierKeys.Shift (для тех, кто пришел сюда в поиске)
Билл Тарбелл,
3
вместо (Control.ModifierKeys & Keys.Shift) != 0одного можно использоватьControl.ModifierKeys.HasFlag(Keys.Shift)
tomuxmon
55

В приведенном ниже коде показано, как обнаружить почти все нажатые в данный момент клавиши, а не только Shiftклавишу.

private KeyMessageFilter m_filter = new KeyMessageFilter();

private void Form1_Load(object sender, EventArgs e)
{
    Application.AddMessageFilter(m_filter);
}


public class KeyMessageFilter : IMessageFilter
{
    private const int WM_KEYDOWN = 0x0100;
    private const int WM_KEYUP = 0x0101;
    private bool m_keyPressed = false;

    private Dictionary<Keys, bool> m_keyTable = new Dictionary<Keys, bool>();

    public Dictionary<Keys, bool> KeyTable
    {
        get { return m_keyTable; }
        private set { m_keyTable = value; }
    }

    public bool IsKeyPressed()
    {
        return m_keyPressed;
    }

    public bool IsKeyPressed(Keys k)
    {
        bool pressed = false;

        if (KeyTable.TryGetValue(k, out pressed))
        {
            return pressed;
        }

        return false;
    }

    public bool PreFilterMessage(ref Message m)
    {
        if (m.Msg == WM_KEYDOWN)
        {
            KeyTable[(Keys)m.WParam] = true;

            m_keyPressed = true;
        }

        if (m.Msg == WM_KEYUP)
        {
            KeyTable[(Keys)m.WParam] = false;

            m_keyPressed = false;
        }

        return false;
    }
}
ясень
источник
1
GetKeyStateбыло бы эффективнее. Нет смысла отслеживать все ключи, когда Windows уже делает это за вас.
SLaks
3
@Slaks, вы догадываетесь, если у вас нет контрольных данных. Более того, GetKeyState сообщит вам состояние клавиши, если вы вообще можете перехватить это событие клавиатуры. Я понимаю вопрос, что OP хочет знать, как получить состояние ключа в любое время . Так что GetKeyState сам по себе бесполезен.
Ash
3
Как именно вы использовали бы это, чтобы показать нажатые клавиши?
Габриэль Райан Нахмиас
Габриэль: Создайте экземпляр KeyMessageFilter как поле в вашей форме. Передайте его Application.AddMessageFilter () в Form_Load. Затем вызовите IsKeyPressed () в этом экземпляре для каждого / любого ключа, который вас интересует.
Эш
@Ash спасибо за ваш ответ - не могли бы вы сделать пример кода для проверки клавиши SHIFT и т. Д. Выше?
BKSpurgeon
24

Вы также можете посмотреть следующее, если используете WPF или ссылаетесь на System.Windows.Input

if (Keyboard.Modifiers == ModifierKeys.Shift)

Пространство имен Keyboard также можно использовать для проверки нажатого состояния других клавиш с помощью Keyboard.IsKeyDown (Key), или, если вы подписываетесь на событие KeyDownEvent или подобное событие, аргументы события содержат список нажатых в данный момент клавиш.

Джефф Уэйн
источник
1
Собственно Keyboard.Modifiers не всегда корректно работают. Пришлось найти трудный путь: discoveringdotnet.alexeyev.org/2008/09/…
Максим Алексеев
За исключением того, что здесь не используются модификаторы Forms, модификаторы System.Windows.Input - это другое пространство имен и каждый раз отлично работает для нас.
Джефф Уэйн,
18

Большинство из этих ответов либо слишком сложны, либо, похоже, мне не подходят (например, System.Windows.Input, похоже, не существует). Затем я нашел пример кода, который отлично работает: http://www.switchonthecode.com/tutorials/winforms-accessing-mouse-and-keyboard-state

На случай, если в будущем страница исчезнет, ​​я размещаю соответствующий исходный код ниже:

using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace MouseKeyboardStateTest
{
  public abstract class Keyboard
  {
    [Flags]
    private enum KeyStates
    {
      None = 0,
      Down = 1,
      Toggled = 2
    }

    [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
    private static extern short GetKeyState(int keyCode);

    private static KeyStates GetKeyState(Keys key)
    {
      KeyStates state = KeyStates.None;

      short retVal = GetKeyState((int)key);

      //If the high-order bit is 1, the key is down
      //otherwise, it is up.
      if ((retVal & 0x8000) == 0x8000)
        state |= KeyStates.Down;

      //If the low-order bit is 1, the key is toggled.
      if ((retVal & 1) == 1)
        state |= KeyStates.Toggled;

      return state;
    }

    public static bool IsKeyDown(Keys key)
    { 
      return KeyStates.Down == (GetKeyState(key) & KeyStates.Down);
    }

    public static bool IsKeyToggled(Keys key)
    { 
      return KeyStates.Toggled == (GetKeyState(key) & KeyStates.Toggled);
    }
  }
}
parsley72
источник
3
System.Windows.Inputсуществуют; для других, которые борются с этим, вам нужно добавить ссылку PresentationCoreи дополнительную ссылку WindowsBaseдля доступа к System.Windows.Input.Keyперечислению. Эту информацию всегда можно найти на MSDN.
Alfie
1
Этот класс должен быть static, а не abstract.
Little Endian
1
Ссылка не работает (404).
Питер Мортенсен
2
«На случай, если в будущем страница исчезнет, ​​я
размещу
12

Начиная с .NET Framework версии 3.0, можно использовать Keyboard.IsKeyDownметод из нового System.Windows.Inputпространства имен. Например:

if (((Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)) && Keyboard.IsKeyDown(Key.F))
{
    // CTRL + F is currently pressed
}

Несмотря на то, что это часть WPF, этот метод отлично работает для приложений WinForm (при условии, что вы добавляете ссылки на PresentationCore.dll и WindowsBase.dll ). К сожалению, версии 3.0 и 3.5 Keyboard.IsKeyDownне работали для приложений WinForm. Поэтому, если вы действительно хотите использовать его в приложении WinForm, вам необходимо настроить таргетинг на .NET Framework 4.0 или более поздней версии, чтобы он работал.

Стивен Доггарт
источник
просто примечание, это только для WPF
Диего Виейра
2
@DiegoVieira На самом деле это неправда. Функциональность была добавлена ​​как часть WPF, и для этого требуется ссылка на эти библиотеки WPF, но Keyboard.IsKeyDownметод работает даже в проекте WinForm.
Стивен Доггарт
Действительно, вам нужно добавить PresentationCore.dll
Диего Виейра
2
Обратите внимание, что это не работает (в WinForms), если нацелено на .Net 3.5 или более раннюю версию, только 4.0+, из-за изменения в реализации Win32KeyboardDevice.GetKeyStatesFromSystem (Key) :(
LMK
@LMK Хороший улов. Я сам это проверил и проверил, что вы сказали. Я обновил свой ответ, чтобы отразить эту информацию. Спасибо!
Стивен Доггарт 08
8

Вы можете использовать P / Invoke до Win32 GetAsyncKeyState, чтобы проверить любую клавишу на клавиатуре.

Вы можете передавать значения из перечисления Keys (например, Keys.Shift) в эту функцию, поэтому для ее добавления требуется всего пара строк кода.

Джейсон Уильямс
источник
Keyboardне распознавался компилятором, но GetAsyncKeystateв user32 работал нормально. Спасибо!
Эйнштейн X. Mystery
5
if ((ModifierKeys == Keys.Control) && ((e.KeyChar & (char)Keys.F) != 0))
{
     // CTRL+F pressed !
}
мессия
источник
3

Лучший способ, который я нашел для управления вводом с клавиатуры в форме Windows Forms, - это обработать его после нажатия клавиши и до того, как сфокусированный элемент управления получит событие. Microsoft поддерживает встроенное Formсвойство -level с именем .KeyPreview, чтобы упростить эту задачу :

public frmForm()
{
    // ...
    frmForm.KeyPreview = true;
    // ...
}

Затем события формы _KeyDown, _KeyPress и / или _KeyUp могут быть маршалированы для доступа к событиям ввода до того, как элемент управления сфокусированной формой когда-либо их увидит, и вы можете применить логику обработчика, чтобы захватить событие там или позволить ему перейти к элементу управления сфокусированной формой ,

Несмотря на то, что она не такая структурно изящная, как архитектура маршрутизации событий XAML , она значительно упрощает управление функциями уровня формы в Winforms. См. Примечания MSDN по KeyPreview для предупреждений .

Hardryv
источник
2
if (Form.ModifierKeys == Keys.Shift)

работает для текстового поля, если приведенный выше код находится в событии keydown формы и никакой другой элемент управления не фиксирует событие keydown для нажатия клавиши.

Также можно остановить дальнейшую обработку ключей:

e.Handled = true;
гг
источник
2
if (Control.ModifierKeys == Keys.Shift)
    //Shift is pressed

Положение курсора x / y - это свойство, а нажатие клавиши (например, щелчок / движение мыши) - это событие. Лучшая практика - позволить интерфейсу управлять событиями. Примерно единственный раз, когда вам понадобится указанное выше, это если вы пытаетесь сделать что-то shift + mouseclick.

Роб Эллиотт
источник