¿Cómo detectar la tecla presionada actualmente?

123

En Windows Forms , puede saber, en cualquier momento, la posición actual del cursor gracias a la clase Cursores .

Lo mismo no parece estar disponible para el teclado. ¿Es posible saber si, por ejemplo, Shiftse presiona la tecla?

¿Es absolutamente necesario rastrear cada notificación de teclado (eventos KeyDown y KeyUp)?

Peter Mortensen
fuente
¿Estás trabajando en un entorno WPF u otra cosa?
epotter
70
@epotter: la segunda palabra indica WinForms.
Will Eddins

Respuestas:

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

Esto también será cierto si Ctrl+ Shiftestá abajo. Si desea verificar si se presiona Shift solo,

if (Control.ModifierKeys == Keys.Shift)

Si está en una clase que hereda Control(como un formulario), puede eliminar elControl.

SLaks
fuente
8
A menos que me falte algo, no ha respondido la pregunta correctamente. El OP pregunta por todas las teclas y utiliza la tecla Shift solo como ejemplo. Entonces, ¿cómo detectar otras teclas como la A a la Z, del 0 al 9, etc.
Ash
2
Dado que aceptó la respuesta, parece que solo necesitaba claves modificadoras. Si desea otras claves, deberá llamar a la GetKeyStatefunción API.
SLaks
2
no necesita GetKeyState. Solo necesita agregar un filtro de mensajes. Mira mi respuesta.
Ash
2
Para una solución WPF que puede usar Keyboard.Modifiers == ModifierKeys.Shift (para aquellos que vinieron aquí en una búsqueda)
Bill Tarbell
3
en lugar de (Control.ModifierKeys & Keys.Shift) != 0uno puede usarControl.ModifierKeys.HasFlag(Keys.Shift)
tomuxmon
55

El siguiente código es cómo detectar casi todas las teclas presionadas actualmente, no solo la 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;
    }
}
Ceniza
fuente
1
GetKeyStateSería más eficiente. No tiene sentido rastrear todas las claves cuando Windows ya lo hace por usted.
SLaks
3
@Slaks, a menos que tenga algunos datos de referencia, está adivinando. Además, GetKeyState le dirá el estado de una tecla, si puede atrapar ese evento de teclado en primer lugar. Mi lectura de la pregunta es que el OP quiere saber cómo obtener el estado de una clave en cualquier momento . Entonces GetKeyState por sí solo es inútil.
Ash
3
¿Cómo exactamente utilizaría esto para mostrar las teclas presionadas?
Gabriel Ryan Nahmias
Gabriel: Cree una instancia de KeyMessageFilter como un campo en su formulario. Pásalo a Application.AddMessageFilter () en Form_Load. Luego llame a IsKeyPressed () en esta instancia para cada / cualquier tecla que le interese.
Ash
@ Ash agradece tu respuesta: ¿podrías hacer un ejemplo de código para verificar la tecla SHIFT, etc., arriba?
BKSpurgeon
23

También puede ver lo siguiente si hace referencia a System.Windows.Input

if (Keyboard.Modifiers == ModifierKeys.Shift)

El espacio de nombres del teclado también se puede usar para verificar el estado presionado de otras teclas con Keyboard.IsKeyDown (Key), o si se está suscribiendo a un evento KeyDownEvent o similar, los argumentos del evento llevan una lista de las teclas presionadas actualmente.

Jeff Wain
fuente
1
En realidad Keyboard.Modifiers no siempre funcionan correctamente. Tenía que encontrar el camino difícil: discoveringdotnet.alexeyev.org/2008/09/…
Maxim Alexeyev
Excepto que esto no está utilizando modificadores de formularios, los modificadores System.Windows.Input es un espacio de nombres diferente y siempre ha funcionado bien para nosotros.
Jeff Wain el
18

La mayoría de estas respuestas son demasiado complicadas o no parecen funcionar para mí (por ejemplo, System.Windows.Input no parece existir). Luego encontré un código de muestra que funciona bien: http://www.switchonthecode.com/tutorials/winforms-accessing-mouse-and-keyboard-state

En caso de que la página desaparezca en el futuro, estoy publicando el código fuente relevante a continuación:

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);
    }
  }
}
perejil72
fuente
3
System.Windows.Inputexiste para otros que luchan con esto, necesita agregar una referencia PresentationCorey una referencia adicional WindowsBasepara acceder a la System.Windows.Input.Keyenumeración. Esta información siempre se puede encontrar en MSDN.
Alfie
1
Se supone que esta clase es static, no abstract.
Little Endian
1
El enlace está roto (404).
Peter Mortensen
2
"En caso de que la página desaparezca en el futuro, publicaré el código fuente correspondiente a continuación"
parsley72
12

Desde .NET Framework versión 3.0, es posible usar el Keyboard.IsKeyDownmétodo desde el nuevo System.Windows.Inputespacio de nombres. Por ejemplo:

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

Aunque es parte de WPF, ese método funciona bien para aplicaciones WinForm (siempre que agregue referencias a PresentationCore.dll y WindowsBase.dll ). Desafortunadamente, sin embargo, las versiones 3.0 y 3.5 del Keyboard.IsKeyDownmétodo no funcionaron para las aplicaciones WinForm. Por lo tanto, si desea usarlo en una aplicación WinForm, tendrá que apuntar a .NET Framework 4.0 o posterior para que funcione.

Steven Doggart
fuente
solo una nota, esto es solo para WPF
Diego Vieira
2
@DiegoVieira En realidad, eso no es cierto. La funcionalidad se agregó como parte de WPF y requiere que se haga referencia a esas bibliotecas de WPF, pero el Keyboard.IsKeyDownmétodo funciona, incluso en un proyecto WinForm.
Steven Doggart
De hecho, debe agregar PresentationCore.dll
Diego Vieira
2
Tenga en cuenta que esto no funciona (en WinForms) si apunta a .Net 3.5 o anterior, solo 4.0+, debido al cambio en la implementación de Win32KeyboardDevice.GetKeyStatesFromSystem (Key) :(
LMK
@LMK Buena captura. Lo probé yo mismo y verifiqué lo que dijiste. Actualicé mi respuesta para reflejar esa información. ¡Gracias!
Steven Doggart
8

Puede P / Invocar hasta Win32 GetAsyncKeyState para probar cualquier tecla del teclado.

Puede pasar valores desde la enumeración Keys (por ejemplo, Keys.Shift) a esta función, por lo que solo se requieren un par de líneas de código para agregarla.

Jason Williams
fuente
Keyboardno fue reconocido por el compilador, pero GetAsyncKeystateen user32 funcionó bien. ¡Gracias!
Einstein X. Mystery
5
if ((ModifierKeys == Keys.Control) && ((e.KeyChar & (char)Keys.F) != 0))
{
     // CTRL+F pressed !
}
Mahdi
fuente
3

La mejor manera que he encontrado para administrar la entrada del teclado en un formulario de Windows Forms es procesarla después de presionar la tecla y antes de que el control enfocado reciba el evento. Microsoft mantiene una Formpropiedad de nivel incorporado llamada .KeyPreview para facilitar esto:

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

Luego, los eventos _KeyDown, _KeyPress y / o _KeyUp del formulario se pueden ordenar para acceder a los eventos de entrada antes de que el control de formulario enfocado los vea, y puede aplicar la lógica del controlador para capturar el evento allí o permitir que pase al control de formulario enfocado .

Aunque no es tan estructuralmente elegante como la arquitectura de enrutamiento de eventos de XAML , hace que la administración de las funciones de nivel de formulario en Winforms sea mucho más simple. Consulte las notas de MSDN en KeyPreview para ver las advertencias.

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

funciona para un cuadro de texto si el código anterior está en el evento keydown del formulario y ningún otro control captura el evento keydown para la tecla down.

También es posible que desee detener el procesamiento de claves adicionales con:

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

La posición del cursor x / y es una propiedad, y una pulsación de tecla (como un clic del mouse / movimiento del mouse) es un evento. La mejor práctica suele ser dejar que la interfaz sea controlada por eventos. Casi la única vez que necesitaría lo anterior es si está tratando de hacer una cosa shift + mouseclick.

Rob Elliott
fuente