Najlepszy sposób na wdrożenie skrótów klawiaturowych w aplikacji Windows Forms?

280

Szukam najlepszego sposobu na implementację typowych skrótów klawiaturowych Windows (na przykład Ctrl+ F, Ctrl+ N) w mojej aplikacji Windows Forms w C #.

Aplikacja ma główny formularz, który obsługuje wiele formularzy podrzędnych (pojedynczo). Gdy użytkownik naciśnie Ctrl+ F, chciałbym wyświetlić niestandardowy formularz wyszukiwania. Formularz wyszukiwania będzie zależał od bieżącego otwartego formularza potomnego w aplikacji.

Myślałem o użyciu czegoś takiego w zdarzeniu ChildForm_KeyDown :

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

Ale to nie działa. Wydarzenie nie uruchamia się nawet po naciśnięciu klawisza. Jakie jest rozwiązanie?

Rockcoder
źródło
To naprawdę dziwne, że Winforms nie wydaje się mieć do tego specyficznej funkcjonalności, tak jak ma to miejsce w natywnym API Windows.
Stewart

Odpowiedzi:

460

Prawdopodobnie zapomniałeś ustawić właściwość KeyPreview formularza na True. Przesłonięcie metody ProcessCmdKey () to ogólne rozwiązanie:

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
źródło
Działa to dobrze, ale wykrywa to dla mnie tylko wtedy, gdy formularz jest aktywnym oknem. Czy ktoś wie, jak go powiązać, aby w dowolnym widoku okna było to możliwe?
Gaʀʀʏ
W niektórych sytuacjach KeyPreviewjest to niezbędne. Na przykład naciśnięcie skrótu jest wymagane, aby stłumić wejście, ale mimo to umożliwić uruchomienie osobnego MenuStripzdarzenia. ProcessCmdKeypodejście wymusiłoby powielenie logiki wyzwalania zdarzeń.
Saul
1
Hmm, nie, moduł obsługi zdarzeń KeyDown formularza różni się znacznie od modułu obsługi zdarzeń Click elementu menu. Nadal robisz dokładnie to samo, albo bezpośrednio wywołując metodę obsługi zdarzeń (nie ma potrzeby, aby była wywoływana wyłącznie przez zdarzenie), albo przekieruj wspólną logikę na osobną metodę.
Hans Passant
14
„Co to Ctrl + F?” LOL
kchad
73

W formularzu głównym

  1. Zestaw KeyPreview na True
  2. Dodaj moduł obsługi zdarzeń KeyDown z następującym kodem

    private void MainForm_KeyDown(object sender, KeyEventArgs e)
    {
        if (e.Control && e.KeyCode == Keys.N)
        {
            SearchForm searchForm = new SearchForm();
            searchForm.Show();
        }
    }
Almir
źródło
4
+ dla, Ustaw KeyPreview na True .. tego brakowało
Usman Younas
20

Najlepszym sposobem jest użycie mnemoników menu, tj. Aby mieć pozycje menu w głównej formie, które zostaną przypisane do odpowiedniego skrótu klawiaturowego. Następnie wszystko inne jest obsługiwane wewnętrznie i wszystko, co musisz zrobić, to zaimplementować odpowiednią akcję, która zostanie wykonana w Clickmodule obsługi zdarzeń tej pozycji menu.

Konrad Rudolph
źródło
1
Problem polega na tym, że główna forma nie korzysta ze zwykłych menu systemu Windows. Wykorzystuje niestandardowy panel nawigacyjny, który służy do wyświetlania formularzy potomnych. Formularze wyszukiwania są wywoływane poprzez kliknięcie ToolStripButton w formularzu potomnym.
Rockcoder
menu mnemonicsprawdziwa próbka?
Kiquenet,
1
@Kiquenet docs.microsoft.com/en-us/dotnet/api/…
Konrad Rudolph
1
Mnemoniki to odmienna koncepcja niż klawisze skrótów i istnieją ważne różnice w ich działaniu. W skrócie, mnemoniki to podkreślone znaki używane do uzyskiwania dostępu do menu, poleceń menu i kontrolek dialogowych za pomocą klawiatury. OTOH, klawisze skrótów są sposobem na dostęp do poleceń bez przechodzenia przez menu, a ponadto mogą to być dowolne naciśnięcia klawiszy, takie jak Ctrl + F lub F5, nieograniczone do klawiszy znaków.
Stewart
1
@Stewart To prawda! Moja odpowiedź nie próbowała sugerować, że wszystkie skróty klawiaturowe są mnemoniczne, a jedynie, że mnemoniki menu są najłatwiejszym sposobem uzyskania tej funkcjonalności. Niestety, jak wyjaśnia komentarz Rockcodera, to rozwiązanie nie wydaje się tutaj odpowiednie. Nadal polecam to rozwiązanie, jeśli jest to możliwe. Ogólnym rozwiązaniem jest akceptowana odpowiedź Hansa.
Konrad Rudolph
11

Możesz nawet spróbować tego przykładu:

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
źródło
8

Jeśli masz menu, możesz zmienić ShortcutKeyswłaściwośćToolStripMenuItem powinna załatwić sprawę.

Jeśli nie, możesz go utworzyć i ustawić jego wartość visiblefalse.

Corin Blaikie
źródło
Nie, nie mam menu. ToolStripButton do wyszukiwania znajduje się w rzeczywistości na formancie BindingNavigator, więc dodanie menu prawdopodobnie nie jest opcją.
Rockcoder,
6

Z głównego formularza musisz:

  • Pamiętaj, aby ustawić KeyPreview na true (domyślnie PRAWDA)
  • Dodaj MainForm_KeyDown (..) - za pomocą którego możesz ustawić dowolne skróty.

Ponadto znalazłem to w google i chciałem udostępnić to tym, którzy wciąż szukają odpowiedzi. (dla globalnych)

Myślę, że musisz używać 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
    }
}

Dalej przeczytaj ten http://www.fluxbytes.com/csharp/how-to-register-a-global-hotkey-for-your-application-in-c/

Juran
źródło
4

Odpowiedź Hansa mogłaby być nieco łatwiejsza dla kogoś nowego, więc oto moja wersja.

Nie musisz oszukiwać KeyPreview, pozostaw to ustawienie false. Aby użyć poniższego kodu, po prostu wklej go poniżej swojego form1_loadi uruchom, F5aby zobaczyć, jak działa:

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
źródło
7
Po pierwsze, zastąpienie ProcessCmdKey jest zalecanym sposobem obsługi naciśnięcia klawisza przeznaczonego do wykonania operacji podobnej do polecenia, o którą prosi OP. Po drugie, źle zrozumiałeś komentarz Hansa o ustawieniu KeyPreview na true; właśnie mówił OP, dlaczego jego technika nie działa. Ustawienie KeyPreview na true nie jest konieczne do używania ProcessCmdKey.
RenniePet
2

W WinForm zawsze możemy uzyskać status klucza kontrolnego poprzez:

bool IsCtrlPressed = (Control.ModifierKeys & Keys.Control) != 0;
sk
źródło