Formularze nie odpowiadają na zdarzenia KeyDown

82

Przez jakiś czas pracowałem nad projektem Windows Forms i zdecydowałem się poeksperymentować ze skrótami klawiaturowymi. Po lekturze doszedłem do wniosku, że muszę po prostu napisać procedurę obsługi zdarzeń i powiązać ją ze zdarzeniem KeyDown formularza:

private void Form1_KeyDown(object sender, KeyEventArgs e)
{
    if (e.Control && e.Alt && e.KeyCode == Keys.O)
    {
        MessageBox.Show("Ctrl+Alt+O: magic!");
    }
}

Zrobiłem to starym dobrym sposobem na otwarcie panelu Właściwości projektanta programu Visual Studio, a następnie dwukrotne kliknięcie zdarzenia KeyDown w moim formularzu, aby wygenerować Form1_KeyDownprocedurę obsługi zdarzeń. Ale podczas testowania mojej aplikacji formularz w ogóle nie reaguje na skrót klawiaturowy Ctrl+ Alt+ O. Projektant programu Visual Studio wygenerował jednak kod, aby powiązać procedurę obsługi zdarzeń z formularzem:

private void InitializeComponent()
{
    // ...

    this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.Form1_KeyDown);

    // ...
}

Więc spróbowałem dodać Console.WriteLine()wywołanie do programu obsługi, aby sprawdzić, czy w ogóle jest wywoływany, ale też bez powodzenia.

Próbowałem również ustawić punkt przerwania w wywołaniu powiązania zdarzenia (pokazanym powyżej) i stwierdziłem, że program osiąga ten punkt przerwania w porządku. Ale żadne punkty przerwania, które ustawiłem w samej definicji metody, nigdy nie są osiągane.

Aby upewnić się, że kilka pierwszych kroków wykonałem poprawnie, spróbowałem je powtórzyć z:

  • Nowa forma w tym samym rozwiązaniu.
    Ten sam problem: formularz nie odpowiada, gdy naciskam skrót klawiaturowy Ctrl+ Alt+, Oa debugger nie wchodzi nawet do modułu obsługi zdarzeń. Próbowałem tego ponownie i działa.

  • Zupełnie nowe rozwiązanie WinForms.
    Działa idealnie: pojawia się okno dialogowe wiadomości ( Console.WriteLine()połączenie również działa).

Więc jestem tu zagubiony. Co uniemożliwia wszystkim formularzom w tym jednym projekcie otrzymywanie zdarzeń KeyDown?

BoltClock
źródło

Odpowiedzi:

174

Czy Twój formularz ma właściwość KeyPreview ustawioną na true?

Form.KeyPreview Property

Pobiera lub ustawia wartość wskazującą, czy formularz otrzyma kluczowe zdarzenia przed przekazaniem zdarzenia do kontrolki, na której jest fokus.

http://msdn.microsoft.com/en-us/library/system.windows.forms.form.keypreview.aspx

STO
źródło
19
To hack, udostępniony, aby zadowolić programistów VB6. Ma problemy z kolejnością wykonywania, zamiast tego zastąp ProcessCmdKey ().
Hans Passant
@HansPassant, nie mogę znaleźć nic wyjaśniającego problemy z kolejnością wykonania. KeyDown + KeyPreview nie zobaczy wszystkich kluczy, co jest wystarczającym problemem, ale jakie są problemy ze zleceniem wykonania?
kdbanman
1
Istnieje wiele nadpisań umożliwiających wykrycie naciśnięć klawiszy. Wykonywane w kolejności, KeyPreview + KeyDown jest martwe jako ostatnie.
Hans Passant
54

Najczęstsza rada dotycząca tego problemu w StackOverflow i MSDN 1 , 2 (w tym zaakceptowana odpowiedź tutaj) jest szybka i łatwa:

KeyDownzdarzenia są wyzwalane Formtak długo, jak jego KeyPreviewwłaściwość jest ustawiona natrue

Jest to wystarczające do większości celów, ale jest ryzykowne z dwóch powodów:

  1. KeyDownprogramy obsługi nie widzą wszystkich kluczy . W szczególności „nie widać naciśnięć klawiszy, które są używane do nawigacji. Podobnie jak klawisze kursora i Tab, Escape i Enter w oknie dialogowym”.

  2. Istnieje kilka różnych sposobów przechwytywania kluczowych wydarzeń i wszystkie one następują po kolei. KeyDownjest obsługiwany jako ostatni . W związku z tym KeyPreviewnie jest to zbyt duży podgląd, a wydarzenie można by wyciszyć na kilku przystankach po drodze.

(Podziękowania dla @HansPassant za te punkty).

Zamiast tego zastąp ProcessCmdKeyw Form:

protected override bool ProcessCmdKey(ref Message msg, Keys keyData) {
    if (keyData == Keys.Up)
    {
        // Handle key at form level.
        // Do not send event to focused control by returning true.
        return true;
    }
  return base.ProcessCmdKey(ref msg, keyData);
}

W ten sposób wszystkie klucze są widoczne dla metody, a metoda jest pierwsza w kolejce, aby zobaczyć zdarzenie.

Zwróć uwagę, że nadal masz kontrolę nad tym, czy aktywne kontrolki widzą KeyDownzdarzenie. Po prostu wróć, trueaby zablokować kolejne KeyDownzdarzenie, zamiast ustawiać KeyPressEventArgs.Handledje truetak, jak w KeyDownprzypadku obsługi zdarzeń. Oto artykuł ze szczegółami.

kdbanman
źródło
1
To jest poprawna odpowiedź, zwłaszcza jeśli okaże się, że PreviewKeyDown w ogóle nie uruchomi się z ustawieniem KeyPreview na true.
Tim
23

Spróbuj ustawić KeyPreviewwłaściwość w formularzu na true. Pomogło mi to w rejestrowaniu naciśnięć klawiszy.

Seb Charrot
źródło