Jak wykryć aktualnie wciśnięty klawisz?

123

W Windows Forms możesz w każdej chwili poznać aktualne położenie kursora dzięki klasie Cursors .

Wydaje się, że ta sama rzecz nie jest dostępna dla klawiatury. Czy można wiedzieć, czy np. ShiftKlawisz jest wciśnięty?

Czy absolutnie konieczne jest śledzenie każdego powiadomienia z klawiatury (zdarzenia KeyDown i KeyUp)?

Peter Mortensen
źródło
Pracujesz w środowisku WPF czy w czymś innym?
epotter
70
@epotter: drugie słowo określa WinForms.
Will Eddins,

Odpowiedzi:

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

Będzie to również prawdą, jeśli Ctrl+ nie Shiftdziała. Jeśli chcesz sprawdzić, czy sam Shift jest wciśnięty,

if (Control.ModifierKeys == Keys.Shift)

Jeśli jesteś w klasie, która dziedziczy Control(na przykład formularz), możesz usunąć rozszerzenieControl.

SLaks
źródło
8
Jeśli czegoś nie brakuje, nie odpowiedziałeś poprawnie na pytanie. OP pyta o wszystkie klawisze i używa klawisza Shift tylko jako przykładu. Jak więc wykryć inne klucze, takie jak A do Z, 0 do 9 itd.
Ash
2
Biorąc pod uwagę, że przyjął odpowiedź, wydaje się, że potrzebował tylko klawiszy modyfikujących. Jeśli chcesz inne klucze, musisz wywołać funkcję GetKeyStateAPI.
SLaks
2
nie ma potrzeby GetKeyState. Wystarczy dodać filtr wiadomości. Zobacz moją odpowiedź.
Ash
2
Dla rozwiązania WPF, którego możesz użyć Keyboard.Modifiers == ModifierKeys.Shift (dla tych, którzy przybyli tutaj na wyszukiwanie)
Bill Tarbell
3
zamiast (Control.ModifierKeys & Keys.Shift) != 0jednego można użyćControl.ModifierKeys.HasFlag(Keys.Shift)
tomuxmon
55

Poniższy kod pokazuje, jak wykryć prawie wszystkie aktualnie wciśnięte klawisze, a nie tylko Shiftklawisz.

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;
    }
}
Popiół
źródło
1
GetKeyStatebyłoby bardziej wydajne. Śledzenie wszystkich kluczy nie ma sensu, gdy system Windows robi to już za Ciebie.
SLaks
3
@Slaks, chyba że masz jakieś dane porównawcze, zgadujesz. Ponadto GetKeyState poinformuje Cię o stanie klucza, jeśli możesz złapać to zdarzenie klawiatury w pierwszej kolejności. Czytam to pytanie, że OP chce wiedzieć, jak uzyskać stan klucza w dowolnym momencie . Więc GetKeyState sam w sobie jest bezużyteczny.
Ash
3
Jak dokładnie wykorzystałbyś to, aby pokazać naciskane klawisze?
Gabriel Ryan Nahmias
Gabriel: Utwórz wystąpienie KeyMessageFilter jako pole w formularzu. Przekaż go do Application.AddMessageFilter () w Form_Load. Następnie wywołaj IsKeyPressed () w tej instancji dla każdego / dowolnego klucza, który Cię interesuje.
Ash
@Ash dzięki za odpowiedź - czy byłbyś w stanie zrobić przykładowy kod do sprawdzenia klawisza SHIFT itp. Powyżej?
BKSpurgeon
23

Jeśli odwołujesz się do System.Windows.Input, możesz również zapoznać się z poniższymi informacjami

if (Keyboard.Modifiers == ModifierKeys.Shift)

Przestrzeń nazw Keyboard może być również używana do sprawdzania stanu wciśnięcia innych klawiszy za pomocą Keyboard.IsKeyDown (Key), lub jeśli subskrybujesz zdarzenie KeyDownEvent lub podobne, argumenty zdarzenia zawierają listę aktualnie wciśniętych klawiszy.

Jeff Wain
źródło
1
Właściwie klawiatura. Modyfikatory nie zawsze działają poprawnie. Musiałem znaleźć trudną drogę: odkrywaniedotnet.alexeyev.org/2008/09/…
Maxim Alexeyev
Z wyjątkiem tego, że nie używa modyfikatorów Forms, modyfikatory System.Windows.Input to inna przestrzeń nazw i za każdym razem działała dobrze.
Jeff Wain
18

Większość z tych odpowiedzi jest albo zbyt skomplikowana, albo wydaje mi się, że nie działają dla mnie (np. System.Windows.Input wydaje się nie istnieć). Potem znalazłem przykładowy kod, który działa dobrze: http://www.switchonthecode.com/tutorials/winforms-accessing-mouse-and-keyboard-state

W przypadku gdyby strona zniknęła w przyszłości, zamieszczam poniżej odpowiedni kod źródłowy:

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);
    }
  }
}
pietruszka72
źródło
3
System.Windows.Inputistnieje; dla innych borykających się z tym musisz dodać odniesienie do PresentationCorei dodatkowe odniesienie do, WindowsBaseaby uzyskać dostęp do System.Windows.Input.Keywyliczenia. Te informacje można zawsze znaleźć w witrynie MSDN.
Alfie
1
Ta klasa ma być static, nie abstract.
Little Endian
1
Link jest uszkodzony (404).
Peter Mortensen
2
„Na wypadek gdyby strona zniknęła w przyszłości, zamieszczam poniżej odpowiedni kod źródłowy”
parsley72
12

Od wersji .NET Framework 3.0 możliwe jest użycie Keyboard.IsKeyDownmetody z nowej System.Windows.Inputprzestrzeni nazw. Na przykład:

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

Mimo że jest częścią WPF, ta metoda działa dobrze dla aplikacji WinForm (pod warunkiem, że dodasz odwołania do PresentationCore.dll i WindowsBase.dll ). Niestety, wersje 3.0 i 3.5 tej Keyboard.IsKeyDownmetody nie działały w aplikacjach WinForm. Dlatego, jeśli chcesz go używać w aplikacji WinForm, musisz być ukierunkowany na .NET Framework 4.0 lub nowszy, aby działał.

Steven Doggart
źródło
uwaga, to jest tylko dla WPF
Diego Vieira
2
@DiegoVieira Właściwie to nieprawda. Funkcjonalność została dodana jako część WPF i wymaga odwoływania się do tych bibliotek WPF, ale Keyboard.IsKeyDownmetoda działa nawet w projekcie WinForm.
Steven Doggart
Rzeczywiście, musisz dodać PresentationCore.dll
Diego Vieira
2
Zauważ, że to nie działa (w WinForms), jeśli jest przeznaczony dla .Net 3.5 lub starszej, tylko 4.0+, z powodu zmiany w implementacji Win32KeyboardDevice.GetKeyStatesFromSystem (Key) :(
LMK
@LMK Niezły chwyt. Sam to przetestowałem i zweryfikowałem, co powiedziałeś. Zaktualizowałem odpowiedź, aby odzwierciedlić te informacje. Dzięki!
Steven Doggart
8

Możesz P / Invoke w dół do Win32 GetAsyncKeyState, aby przetestować dowolny klawisz na klawiaturze.

Do tej funkcji można przekazywać wartości z wyliczenia Keys (np. Keys.Shift), więc dodanie jej wymaga tylko kilku wierszy kodu.

Jason Williams
źródło
Keyboardnie został rozpoznany przez kompilator, ale GetAsyncKeystatew user32 działał dobrze. Dzięki!
Einstein X. Mystery
5
if ((ModifierKeys == Keys.Control) && ((e.KeyChar & (char)Keys.F) != 0))
{
     // CTRL+F pressed !
}
Mahdi
źródło
3

Najlepszym sposobem, w jaki udało mi się zarządzać wprowadzaniem danych z klawiatury w formularzu Windows Forms, jest przetworzenie go po naciśnięciu klawisza i przed odebraniem zdarzenia przez aktywną kontrolkę. Firma Microsoft utrzymuje wbudowaną Formwłaściwość poziomu o nazwie .KeyPreview, aby ułatwić to precyzyjne:

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

Następnie zdarzenia _KeyDown, _KeyPress i / lub _KeyUp formularza można zorganizować w celu uzyskania dostępu do zdarzeń wejściowych, zanim skoncentrowana kontrolka formularza kiedykolwiek je zobaczy, i można zastosować logikę obsługi, aby przechwycić zdarzenie w tym miejscu lub zezwolić na przejście do skoncentrowanej kontrolki formularza .

Chociaż nie jest tak strukturalnie wdzięczny jak architektura routingu zdarzeń XAML, znacznie upraszcza zarządzanie funkcjami na poziomie formularza w WinForm. Zobacz uwagi MSDN dotyczące KeyPreview, aby poznać zastrzeżenia.

Hardryv
źródło
2
if (Form.ModifierKeys == Keys.Shift)

działa dla pola tekstowego, jeśli powyższy kod znajduje się w zdarzeniu keydown formularza i żadna inna kontrolka nie przechwytuje zdarzenia keydown dla klawisza w dół.

Można również chcieć zatrzymać dalsze przetwarzanie klucza za pomocą:

e.Handled = true;
gg
źródło
2
if (Control.ModifierKeys == Keys.Shift)
    //Shift is pressed

Pozycja kursora x / y jest właściwością, a naciśnięcie klawisza (podobnie jak kliknięcie myszą / przesunięcie myszy) jest zdarzeniem. Najlepszą praktyką jest zazwyczaj pozwalanie, aby interfejs był sterowany zdarzeniami. Prawie jedyny przypadek, w którym będziesz potrzebować powyższego, to sytuacja, w której próbujesz wykonać zmianę + kliknięcie myszą.

Rob Elliott
źródło