Cara terbaik untuk mengimplementasikan pintasan keyboard di aplikasi Windows Forms?

280

Saya mencari cara terbaik untuk mengimplementasikan pintasan keyboard Windows umum (misalnya Ctrl+ F, Ctrl+ N) di aplikasi Windows Forms saya di C #.

Aplikasi ini memiliki formulir utama yang menampung banyak formulir anak (satu per satu). Ketika seorang pengguna menekan Ctrl+ F, saya ingin menampilkan formulir pencarian khusus. Formulir pencarian akan tergantung pada formulir anak terbuka saat ini dalam aplikasi.

Saya sedang berpikir untuk menggunakan sesuatu seperti ini di acara ChildForm_KeyDown :

   if (e.KeyCode == Keys.F && Control.ModifierKeys == Keys.Control)
        // Show search form

Tetapi ini tidak berhasil. Acara bahkan tidak menyala ketika Anda menekan tombol. Apa solusinya?

Rockcoder
sumber
Sangat aneh bahwa Winforms tampaknya tidak memiliki fungsi khusus untuk ini seperti Windows API asli.
Stewart

Jawaban:

460

Anda mungkin lupa mengatur properti KeyPreview formulir ke True. Mengganti metode ProcessCmdKey () adalah solusi umum:

protected override bool ProcessCmdKey(ref Message msg, Keys keyData) {
  if (keyData == (Keys.Control | Keys.F)) {
    MessageBox.Show("What the Ctrl+F?");
    return true;
  }
  return base.ProcessCmdKey(ref msg, keyData);
}
Hans Passant
sumber
Ini berfungsi dengan baik, tetapi hanya mendeteksi untuk saya jika formnya adalah jendela aktif. Adakah yang tahu cara mengikatnya sehingga dalam tampilan jendela apa pun dimungkinkan?
Gaʀʀʏ
Dalam beberapa situasi KeyPreviewsangat diperlukan. Sebagai contoh, ketika menekan jalan pintas diperlukan untuk menekan input tetapi tetap membiarkan MenuStripacara terpisah menyala. ProcessCmdKeyPendekatan akan memaksa duplikasi logika penembakan acara.
Saul
1
Hmm, tidak, event handler KeyDown form cukup berbeda dari handler event klik item menu. Anda masih melakukan cara yang sama persis, baik memanggil metode event handler secara langsung (tidak perlu untuk itu secara eksklusif dipanggil oleh suatu peristiwa) atau refactor logika umum menjadi metode yang terpisah.
Hans Passant
14
"Apa Ctrl + F?" LOL
kchad
73

Di formulir Utama Anda

  1. Setel KeyPreviewke True
  2. Tambahkan KeyDown event handler dengan kode berikut

    private void MainForm_KeyDown(object sender, KeyEventArgs e)
    {
        if (e.Control && e.KeyCode == Keys.N)
        {
            SearchForm searchForm = new SearchForm();
            searchForm.Show();
        }
    }
    
Almir
sumber
4
+ untuk, Set KeyPreview ke True .. tidak ada
Usman Younas
20

Cara terbaik adalah dengan menggunakan menu mnemonics, yaitu memiliki entri menu dalam bentuk utama Anda yang ditugaskan pintasan keyboard yang Anda inginkan. Kemudian semua hal lain ditangani secara internal dan yang harus Anda lakukan adalah mengimplementasikan tindakan yang sesuai yang dijalankan dalam Clickevent handler dari entri menu tersebut.

Konrad Rudolph
sumber
1
Masalahnya adalah bahwa formulir utama tidak menggunakan menu windows umum. Ini menggunakan panel navigasi khusus yang digunakan untuk menampilkan formulir anak. Formulir pencarian dipanggil dengan mengklik ToolStripButton pada formulir anak.
Rockcoder
menu mnemonicssampel nyata?
Kiquenet
1
@Kiquenet docs.microsoft.com/en-us/dotnet/api/…
Konrad Rudolph
1
Mnemonik adalah konsep yang berbeda dari tombol pintas, dan ada perbedaan penting dalam cara mereka beroperasi. Singkatnya, mnemonik adalah karakter yang digarisbawahi yang digunakan untuk mengakses menu, perintah menu dan kontrol dialog menggunakan keyboard. OTOH, tombol pintas adalah cara untuk mengakses perintah tanpa melalui menu, dan terlebih lagi mereka dapat menekan tombol sewenang-wenang seperti Ctrl + F atau F5, tidak terbatas pada tombol karakter.
Stewart
1
@ Mulai Itu benar! Jawaban saya tidak berusaha menyiratkan bahwa semua pintasan keyboard adalah mnemonik, hanya bahwa menu mnemonik adalah cara termudah untuk mendapatkan fungsi ini. Sayangnya, seperti yang diklarifikasi oleh komentar Rockcoder, solusi ini sepertinya tidak sesuai di sini. Saya masih merekomendasikannya sebagai solusi yang lebih disukai, jika memungkinkan. Untuk solusi umum, jawaban Hans yang diterima adalah jalan ke depan.
Konrad Rudolph
11

Anda bahkan dapat mencoba contoh ini:

public class MDIParent : System.Windows.Forms.Form
{
    public bool NextTab()
    {
         // some code
    }

    public bool PreviousTab()
    {
         // some code
    }

    protected override bool ProcessCmdKey(ref Message message, Keys keys)
    {
        switch (keys)
        {
            case Keys.Control | Keys.Tab:
              {
                NextTab();
                return true;
              }
            case Keys.Control | Keys.Shift | Keys.Tab:
              {
                PreviousTab();
                return true;
              }
        }
        return base.ProcessCmdKey(ref message, keys);
    }
}

public class mySecondForm : System.Windows.Forms.Form
{
    // some code...
}
Shilpa
sumber
8

Jika Anda memiliki menu maka mengubah ShortcutKeysproperti ToolStripMenuItemharus melakukan trik.

Jika tidak, Anda bisa membuatnya dan mengatur visiblepropertinya menjadi false.

Corin Blaikie
sumber
Tidak, saya tidak punya menu. ToolStripButton untuk pencarian sebenarnya terletak pada kontrol BindingNavigator, jadi menambahkan menu mungkin bukan pilihan.
Rockcoder
6

Dari Formulir utama, Anda harus:

  • Pastikan Anda mengatur KeyPreview menjadi true (TRUE secara default)
  • Tambahkan MainForm_KeyDown (..) - dimana Anda dapat mengatur pintasan yang Anda inginkan di sini.

Selain itu, saya telah menemukan ini di google dan saya ingin membagikan ini kepada mereka yang masih mencari jawaban. (untuk global)

Saya pikir Anda harus menggunakan user32.dll

protected override void WndProc(ref Message m)
{
    base.WndProc(ref m);

    if (m.Msg == 0x0312)
    {
        /* Note that the three lines below are not needed if you only want to register one hotkey.
         * The below lines are useful in case you want to register multiple keys, which you can use a switch with the id as argument, or if you want to know which key/modifier was pressed for some particular reason. */

        Keys key = (Keys)(((int)m.LParam >> 16) & 0xFFFF);                  // The key of the hotkey that was pressed.
        KeyModifier modifier = (KeyModifier)((int)m.LParam & 0xFFFF);       // The modifier of the hotkey that was pressed.
        int id = m.WParam.ToInt32();                                        // The id of the hotkey that was pressed.


        MessageBox.Show("Hotkey has been pressed!");
        // do something
    }
}

Bacalah lebih lanjut http://www.fluxbytes.com/csharp/how-to-register-a-global-hotkey-for-your-application-in-c/

Juran
sumber
4

Jawaban Hans dapat dibuat sedikit lebih mudah bagi seseorang yang baru dalam hal ini, jadi inilah versi saya.

Anda tidak perlu dibodohi KeyPreview, biarkan diatur false. Untuk menggunakan kode di bawah ini, cukup tempel di bawah form1_loaddan jalankan dengan F5untuk melihatnya berfungsi:

protected override void OnKeyPress(KeyPressEventArgs ex)
{
    string xo = ex.KeyChar.ToString();

    if (xo == "q") //You pressed "q" key on the keyboard
    {
        Form2 f2 = new Form2();
        f2.Show();
    }
}
Abhishek Jha
sumber
7
Pertama, mengesampingkan ProcessCmdKey adalah cara yang disarankan untuk menangani penekanan tombol yang dimaksudkan untuk melakukan beberapa operasi seperti perintah, yang merupakan pertanyaan OP. Kedua, Anda salah memahami komentar Hans tentang pengaturan KeyPreview menjadi true; dia hanya memberi tahu OP mengapa tekniknya tidak berhasil. Mengatur KeyPreview ke true tidak diperlukan untuk menggunakan ProcessCmdKey.
RenniePet
2

Di WinForm, kami selalu bisa mendapatkan status Kunci Kontrol dengan:

bool IsCtrlPressed = (Control.ModifierKeys & Keys.Control) != 0;
sk
sumber