Como detectar a tecla atualmente pressionada?

123

No Windows Forms , você pode saber, a qualquer momento, a posição atual do cursor graças aos Cursores classe .

A mesma coisa não parece estar disponível para o teclado. É possível saber se, por exemplo, oShift tecla está pressionada?

É absolutamente necessário rastrear todas as notificações do teclado (eventos KeyDown e KeyUp)?

Peter Mortensen
fonte
Você está trabalhando em um ambiente WPF ou algo mais?
epotter 8/07/09
70
@epotter: A segunda palavra indica WinForms.
Will Eddins

Respostas:

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

Isso também será verdadeiro se Ctrl+ Shiftestiver desativado. Se você quiser verificar se Shift sozinho está pressionado,

if (Control.ModifierKeys == Keys.Shift)

Se você estiver em uma classe que herda Control(como um formulário), você pode remover oControl.

SLaks
fonte
8
A menos que esteja faltando alguma coisa, você não respondeu à pergunta corretamente. O OP está perguntando sobre todas as teclas e usou a tecla Shift como exemplo. Então, como você detectar outras chaves tais como A a Z, 0 a 9 etc.
Ash
2
Dado que ele aceitou a resposta, parece que ele só precisava de teclas modificadoras. Se você quiser outras chaves, precisará chamar a GetKeyStatefunção API.
SLaks
2
não há necessidade de GetKeyState. Você só precisa adicionar um filtro de mensagens. Veja minha resposta.
Ash
2
Para uma solução WPF você pode usar Keyboard.Modifiers == ModifierKeys.Shift (para aqueles que vieram aqui em busca)
Bill Tarbell
3
em vez de (Control.ModifierKeys & Keys.Shift) != 0um pode usarControl.ModifierKeys.HasFlag(Keys.Shift)
tomuxmon
55

O código abaixo é como detectar quase todas as teclas atualmente pressionadas, não apenas a Shifttecla.

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;
    }
}
Cinza
fonte
1
GetKeyStateseria mais eficiente. Não faz sentido rastrear todas as chaves quando o Windows já faz isso por você.
Slaks
3
@Slaks, a menos que você tenha alguns dados de referência, você está supondo. Além disso, o GetKeyState informará o estado de uma tecla, se você puder capturar esse evento de teclado em primeiro lugar. Minha leitura da pergunta é que o OP quer saber como obter o estado de uma chave a qualquer momento . Portanto, o GetKeyState por si só é inútil.
Ash
3
Como exatamente você utilizaria isso para mostrar as teclas pressionadas?
Gabriel Ryan Nahmias 11/11
Gabriel: Crie uma instância do KeyMessageFilter como um campo no seu formulário. Passe para Application.AddMessageFilter () em Form_Load. Em seguida, chamar IsKeyPressed () nessa instância para cada / qualquer tecla que você está interessado.
Ash
@Ash obrigado pela sua resposta - você poderia fazer um exemplo de código para verificar a tecla SHIFT etc. acima?
BKSpurgeon
23

Você também pode observar o seguinte se fizer referência a System.Windows.Input

if (Keyboard.Modifiers == ModifierKeys.Shift)

O espaço para nome do teclado também pode ser usado para verificar o estado pressionado de outras teclas com Keyboard.IsKeyDown (Key) ou, se você estiver assinando um KeyDownEvent ou evento semelhante, os argumentos do evento exibem uma lista de teclas pressionadas no momento.

Jeff Wain
fonte
1
Na verdade, Keyboard.Modifiers nem sempre funcionam corretamente. Tive que encontrar o caminho mais difícil: discoveringdotnet.alexeyev.org/2008/09/… #
Maxim Alexeyev
Exceto que este não está usando modificadores de formulários, os modificadores System.Windows.Input são um espaço para nome diferente e sempre funcionavam bem para nós.
1011 Jeff Wain
18

A maioria dessas respostas é muito complicada ou parece não funcionar para mim (por exemplo, System.Windows.Input parece não existir). Então encontrei um código de exemplo que funciona bem: http://www.switchonthecode.com/tutorials/winforms-accessing-mouse-and-keyboard-state

Caso a página desapareça no futuro, estou postando o código-fonte relevante abaixo:

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
fonte
3
System.Windows.Inputexiste; para outras pessoas que lutam com isso, é necessário adicionar uma referência PresentationCoree uma referência adicional WindowsBasepara acessar a System.Windows.Input.Keyenumeração. Esta informação sempre pode ser encontrada no MSDN.
Alfie
1
Esta classe deveria ser static, não abstract.
Little Endian
1
O link está quebrado (404).
Peter Mortensen
2
"Caso a página desapareça no futuro, eu estou postando o código fonte relevante abaixo"
parsley72 28/02
12

Desde o .NET Framework versão 3.0, é possível usar o Keyboard.IsKeyDownmétodo no novo System.Windows.Inputespaço para nome. Por exemplo:

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

Embora faça parte do WPF, esse método funciona bem para aplicativos WinForm (desde que você adicione referências ao PresentationCore.dll e WindowsBase.dll ). Infelizmente, no entanto, as versões 3.0 e 3.5 do Keyboard.IsKeyDownmétodo não funcionaram para aplicativos WinForm. Portanto, se você quiser usá-lo em um aplicativo WinForm, precisará direcionar o .NET Framework 4.0 ou posterior para que ele funcione.

Steven Doggart
fonte
apenas uma nota, isto é para WPF única
Diego Vieira
2
@DiegoVieira Na verdade, isso não é verdade. A funcionalidade foi adicionada como parte do WPF e requer que essas bibliotecas do WPF sejam referenciadas, mas o Keyboard.IsKeyDownmétodo funciona, mesmo em um projeto WinForm.
9183 Steven Doggart
Na verdade, você tem que adicionar PresentationCore.dll
Diego Vieira
2
Observe que isso não funciona (no WinForms) se estiver direcionando o .Net 3.5 ou anterior, apenas 4.0+, devido a alterações na implementação do Win32KeyboardDevice.GetKeyStatesFromSystem (Key) :(
LMK
@LMK Boa captura. Eu mesmo testei e verifiquei o que você disse. Atualizei minha resposta para refletir essas informações. Obrigado!
Steven Doggart
8

Você pode P / Invoke até o Win32 GetAsyncKeyState para testar qualquer tecla do teclado.

Você pode passar valores da enumeração Keys (por exemplo, Keys.Shift) para essa função, portanto, são necessárias apenas algumas linhas de código para adicioná-lo.

Jason Williams
fonte
Keyboardnão foi reconhecido pelo compilador, mas GetAsyncKeystateno user32 funcionou bem. Obrigado!
Einstein X. Mystery
5
if ((ModifierKeys == Keys.Control) && ((e.KeyChar & (char)Keys.F) != 0))
{
     // CTRL+F pressed !
}
Mahdi
fonte
3

A melhor maneira que encontrei para gerenciar a entrada do teclado em um formulário do Windows Forms é processá-la após o pressionamento de tecla e antes do controle focado receber o evento. A Microsoft mantém uma Formpropriedade interna de nível chamada .KeyPreview para facilitar esta coisa precisa:

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

Os eventos _KeyDown, _KeyPress e / ou _KeyUp do formulário podem ser empacotados para acessar eventos de entrada antes que o controle de formulário focado os veja, e você pode aplicar a lógica do manipulador para capturar o evento ou permitir que ele passe para o controle de formulário focado .

Embora não seja tão estruturalmente elegante quanto a arquitetura de roteamento de eventos do XAML , ela simplifica muito o gerenciamento de funções no nível do formulário no Winforms. Veja as notas do MSDN no KeyPreview para obter advertências.

Hardryv
fonte
2
if (Form.ModifierKeys == Keys.Shift)

funciona para uma caixa de texto se o código acima estiver no evento de pressionamento de tecla do formulário e nenhum outro controle captura o evento de pressionamento de tecla da tecla pressionada.

Também pode-se desejar interromper o processamento de chaves com:

e.Handled = true;
gg
fonte
2
if (Control.ModifierKeys == Keys.Shift)
    //Shift is pressed

A posição x / y do cursor é uma propriedade e um pressionamento de tecla (como um clique do mouse / mouse) é um evento. A melhor prática é geralmente permitir que a interface seja orientada a eventos. A única vez em que você precisaria do descrito acima é se você está tentando fazer algo com o turno + clique com o mouse.

Rob Elliott
fonte