Bagaimana cara mendeteksi tombol yang saat ini ditekan?

123

Pada Windows Forms , Anda bisa tahu, setiap saat, posisi saat ini berkat kursor ke Cursors kelas.

Hal yang sama sepertinya tidak tersedia untuk keyboard. Apakah mungkin untuk mengetahui jika, misalnya, Shifttombolnya ditekan?

Apakah benar-benar perlu untuk melacak setiap notifikasi keyboard (acara KeyDown dan KeyUp)?

Peter Mortensen
sumber
Apakah Anda bekerja di lingkungan WPF atau sesuatu yang lain?
epotter
70
@epotter: Kata kedua menyatakan WinForms.
Will Eddins

Jawaban:

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

Ini juga akan benar jika Ctrl+ Shiftturun. Jika Anda ingin memeriksa apakah Shift saja yang ditekan,

if (Control.ModifierKeys == Keys.Shift)

Jika Anda berada di kelas yang mewarisi Control(seperti formulir), Anda dapat menghapus fileControl.

SLaks
sumber
8
Kecuali saya melewatkan sesuatu, Anda belum menjawab pertanyaan dengan benar. OP menanyakan tentang semua tombol dan menggunakan tombol Shift sebagai contoh saja. Jadi, bagaimana Anda mendeteksi kunci lain seperti A hingga Z, 0 hingga 9, dll.
Ash
2
Mengingat bahwa dia menerima jawabannya, tampaknya dia hanya membutuhkan kunci pengubah. Jika Anda menginginkan kunci lain, Anda harus memanggil GetKeyStatefungsi API.
SLaks
2
tidak perlu GetKeyState. Anda hanya perlu menambahkan filter pesan. Lihat jawaban saya.
Ash
3
Untuk solusi WPF yang dapat Anda gunakan Keyboard.Modifiers == ModifierKeys.Shift (bagi mereka yang datang ke sini untuk mencari)
Bill Tarbell
3
alih-alih yang (Control.ModifierKeys & Keys.Shift) != 0dapat digunakanControl.ModifierKeys.HasFlag(Keys.Shift)
tomuxmon
55

Kode di bawah ini adalah cara mendeteksi hampir semua tombol yang saat ini ditekan, bukan hanya tombolnya 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;
    }
}
Abu
sumber
1
GetKeyStateakan lebih efisien. Tidak ada gunanya melacak semua kunci ketika Windows sudah melakukannya untuk Anda.
SLaks
3
@ Slaks, kecuali Anda memiliki beberapa data tolok ukur, Anda hanya menebak-nebak. Selain itu, GetKeyState akan memberi tahu Anda status kunci, jika Anda dapat menjebak acara keyboard itu di tempat pertama. Saya membaca pertanyaan itu adalah bahwa OP ingin tahu bagaimana mendapatkan status kunci setiap saat . Jadi GetKeyState sendiri tidak berguna.
Ash
3
Bagaimana tepatnya Anda memanfaatkan ini untuk menunjukkan tombol yang ditekan?
Gabriel Ryan Nahmias
Gabriel: Buat instance KeyMessageFilter sebagai bidang di formulir Anda. Berikan ke Application.AddMessageFilter () di Form_Load. Kemudian panggil IsKeyPressed () pada contoh ini untuk setiap / kunci apa pun yang Anda minati.
Ash
@Ash terima kasih atas jawaban Anda - apakah Anda dapat melakukan contoh kode untuk memeriksa tombol SHIFT dll. Di atas?
BKSpurgeon
24

Anda juga dapat melihat berikut ini jika Anda menggunakan WPF atau referensi System.Windows.Input

if (Keyboard.Modifiers == ModifierKeys.Shift)

Ruang nama Keyboard juga dapat digunakan untuk memeriksa status tombol lain yang ditekan dengan Keyboard.IsKeyDown (Key), atau jika Anda berlangganan KeyDownEvent atau acara serupa, argumen acara membawa daftar tombol yang saat ini ditekan.

Jeff Wain
sumber
1
Sebenarnya Keyboard.Modifiers tidak selalu berfungsi dengan baik. Harus menemukan cara yang sulit: discoveringdotnet.alexeyev.org/2008/09/…
Maxim Alexeyev
Kecuali ini tidak menggunakan pengubah Formulir, pengubah System.Windows.Input adalah namespace yang berbeda dan telah berfungsi dengan baik untuk kami setiap saat.
Jeff Wain
18

Sebagian besar jawaban ini terlalu rumit atau sepertinya tidak berhasil untuk saya (misalnya System.Windows.Input sepertinya tidak ada). Kemudian saya menemukan beberapa kode contoh yang berfungsi dengan baik: http://www.switchonthecode.com/tutorials/winforms-accessing-mouse-and-keyboard-state

Jika halaman tersebut hilang di masa mendatang, saya memposting kode sumber yang relevan di bawah ini:

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);
    }
  }
}
peterseli72
sumber
3
System.Windows.Inputada; bagi orang lain yang berjuang dengan ini, Anda perlu menambahkan referensi PresentationCore, dan referensi tambahan WindowsBaseuntuk mengakses System.Windows.Input.Keypencacahan. Info ini selalu dapat ditemukan di MSDN.
Alfie
1
Kelas ini seharusnya static, bukan abstract.
Little Endian
1
Tautannya rusak (404).
Peter Morten
2
"Jika halaman menghilang di masa mendatang, saya memposting kode sumber yang relevan di bawah ini"
parsley72
12

Sejak .NET Framework versi 3.0, dimungkinkan untuk menggunakan Keyboard.IsKeyDownmetode dari System.Windows.Inputnamespace baru . Misalnya:

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

Meskipun itu bagian dari WPF, metode itu berfungsi dengan baik untuk aplikasi WinForm (asalkan Anda menambahkan referensi ke PresentationCore.dll dan WindowsBase.dll ). Sayangnya, bagaimanapun, versi 3.0 dan 3.5 dari Keyboard.IsKeyDownmetode ini tidak bekerja untuk aplikasi WinForm. Oleh karena itu, jika Anda ingin menggunakannya dalam aplikasi WinForm, Anda harus menargetkan .NET Framework 4.0 atau yang lebih baru agar dapat berfungsi.

Steven Doggart
sumber
hanya catatan, ini untuk WPF saja
Diego Vieira
2
@DiegoVieira Sebenarnya, itu tidak benar. Fungsionalitas tersebut ditambahkan sebagai bagian dari WPF, dan memerlukan referensi pustaka WPF tersebut, tetapi Keyboard.IsKeyDownmetode ini berfungsi, bahkan dalam proyek WinForm.
Steven Doggart
Memang, Anda harus menambahkan PresentationCore.dll
Diego Vieira
2
Perhatikan bahwa ini tidak berfungsi (di WinForms) jika menargetkan .Net 3.5 atau yang lebih lama, hanya 4.0+, karena perubahan dalam implementasi Win32KeyboardDevice.GetKeyStatesFromSystem (Key) :(
LMK
@ L Saya mengujinya sendiri dan memverifikasi apa yang Anda katakan. Saya memperbarui jawaban saya untuk mencerminkan informasi itu. Terima kasih!
Steven Doggart
8

Anda dapat P / Invoke ke Win32 GetAsyncKeyState untuk menguji tombol apa saja pada keyboard.

Anda dapat mengirimkan nilai dari enum Keys (mis. Keys.Shift) ke fungsi ini, jadi hanya memerlukan beberapa baris kode untuk menambahkannya.

Jason Williams
sumber
Keyboardtidak dikenali oleh kompiler, tetapi GetAsyncKeystatedi user32 bekerja dengan baik. Terima kasih!
Einstein X. Mystery
5
if ((ModifierKeys == Keys.Control) && ((e.KeyChar & (char)Keys.F) != 0))
{
     // CTRL+F pressed !
}
Mahdi
sumber
3

Cara terbaik yang saya temukan untuk mengelola input keyboard pada formulir Windows Forms adalah dengan memprosesnya setelah penekanan tombol dan sebelum kontrol terfokus menerima acara tersebut. Microsoft mempertahankan Formproperti tingkat bawaan bernama .KeyPreview untuk memfasilitasi hal yang tepat ini:

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

Kemudian peristiwa _KeyDown, _KeyPress, dan / atau _KeyUp formulir dapat diatur untuk mengakses peristiwa masukan sebelum kontrol formulir terfokus pernah melihatnya, dan Anda dapat menerapkan logika pengendali untuk menangkap peristiwa di sana atau membiarkannya melewati ke kontrol formulir terfokus .

Meskipun secara struktural tidak anggun seperti arsitektur perutean peristiwa XAML , hal itu membuat pengelolaan fungsi tingkat bentuk di Winforms jauh lebih sederhana. Lihat catatan MSDN di KeyPreview untuk peringatan.

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

tidak berfungsi untuk kotak teks jika kode di atas berada dalam peristiwa keydown formulir dan tidak ada kontrol lain yang menangkap peristiwa keydown untuk tombol bawah.

Juga seseorang mungkin ingin menghentikan pemrosesan kunci lebih lanjut dengan:

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

Posisi x / y kursor adalah properti, dan penekanan tombol (seperti klik / mousemove mouse) adalah peristiwa. Praktik terbaik biasanya membiarkan antarmuka digerakkan oleh peristiwa. Tentang satu-satunya waktu yang Anda perlukan di atas adalah jika Anda mencoba melakukan shift + klik mouse.

Rob Elliott
sumber