Управление формой с клавиатуры

Необходимо перехватывать нажатые на клавиатуре клавиши. Для этого обрабатываю событие KeyDown:

        private void Form1_KeyDown(object sender, KeyEventArgs e)
        {
            System.Diagnostics.Debug.WriteLine(e.KeyCode);
        }

Однако, если положить на форму такие компоненты как Button, ListBox, ListView, TreeView, TextBox (возможно, ещё какие-то), то событие тупо не приходит, потому что форма перестаёт принимать фокус.

Form.KeyPreview Property (System.Windows.Forms) | Microsoft Docs

Хуки надо пользовать. Они захватят независимо от фокуса приложения.

Ну это смотря что надо.
Вряд ли Ctrl+S текстового редактора должен срабатывать когда в фокусе совсем не он.

KeyPreview включено. Делаю так:

        private void Form1_MouseDown(object sender, MouseEventArgs e)
        {
            ActiveControl = null;
            Focus();
        }

        private void Form1_KeyDown(object sender, KeyEventArgs e)
        {
            e.Handled = ActiveControl == null;
            System.Diagnostics.Debug.WriteLine(e.KeyCode);
        }

Но когда я нажимаю клавишу вверх, то фокус переходит в ListBox (где тут логика???) и в результате ListBox и форма начинают принимать одни и те же сообщения (события) от клавиатуры.
Где написано, что при нажатии вверх фокус должен переходить на ListBox ListView EditBox (смотря что лежит) и как это выключить?

:man_facepalming:
А хуки-то мне зачем? :dizzy_face: :dizzy_face: :dizzy_face: Я же не кейлоггер пишу.

Тут видимо должно было быть !=

Не, == вроде правильно :thinking: Если ни один контрол не активен, то ставим true.
Но почему при нажатии вверх условие не выполняется? :thinking:
Видимо, нажатие вверх активирует ListBox еще до прихода KeyDown в форму :thinking: Но почему и как это выключить?
Я понял. Когда нажимаешь стрелки первый раз , то событие KeyDown вообще не приходит, но зато активируется ListBox (но это зависит от того, какую стрелку нажал и какой индекс стоял у ListBox). А потом, когда ListBox уже стал активен, форма начинает реагировать на стрелки. Но и индекс в ListBox меняется.
Это баг или фича? Если фича, то нафига? Если баг, то почему бы его не пофиксить? :man_shrugging:

Оно просто не null изначально.

Кто не null, ActiveControl? Я же делаю его null, иначе форма не принимает фокус и KeyDown ей не приходит.

По-моему это не убирает фокус.

А вызов фокуса на самой форме вообще хз что делать должен.

На это влияет KeyPreview.

Всё приходит.

Peek 2021-06-29 11-40

Кроме таба, его можно добавить так Tab and KeyDown event
Но обычно хватает стандартного поведения, иначе как клавиатурой фокус менять.

Убирает. Добавьте кнопку и по ней будет видно. С кнопки точно убирает.

В параллельной вселенной он должен делать SetFocus(formHandle); как на WinAPI и фокус не должен сам никуда слетать.

сделайте

        private void form1_MouseDown(object sender, MouseEventArgs e)
        {
            ActiveControl = null;
            Focus();
        }

и проверьте.

Стрелки видимо меняют фокус если текущий контрол их не обрабатывает.

А зачем так делать? Просто e.Handled = true; если обработали клавиши.

Мне надо обрабатывать нажатия только если ни один контрол не активен. Конечно, можно, наверное, при каждом нажатии как-то перебрать все контролы и проверить их активность. Но это опять костыли костыли костыли.
Да и при том, при нажатии на стрелки фокус всё-равно куда-то уходит, так что, не помогло бы.

Так это делается как и

        private void Form1_KeyDown(object sender, KeyEventArgs e)
        {
            Debug.WriteLine(e.KeyCode);
            Debug.WriteLine($"ActiveControl = {ActiveControl}");
            if (ActiveControl != null)
            {
                return;
            }

            if (e.KeyCode == Keys.Up)
            {
                Debug.WriteLine("⬆️");

                e.Handled = true;
            }
        }

        private void Form1_MouseDown(object sender, MouseEventArgs e)
        {
            ActiveControl = null;
            Focus();
        }

        protected override bool IsInputKey(Keys keyData)
        {
            return keyData == Keys.Up || base.IsInputKey(keyData);
        }

:thinking:
Еще не пробовал, но в чём тут магия? Почему так контролы не будут активироваться при нажатии стрелок?
Проверил - абсолютно то же самое. Нажимаю на стрелки и ListBox активируется. Зачем это сделано и как отключить?
И, кстати, по ссылке там совершенно другая проблема, с данной темой никак не связанная.
Или имелось ввиду создать наследников от ListBox / Button и этот код туда вставить? Но похоже, что нет.

Листбокс не причем.

Задача же

Если добавить IsInputKey у формы, то когда нет фокуса стрелки будут попадать в KeyDown тоже.

Peek 2021-06-29 16-21

У листбокса и текстбокса оно и так есть и ловится.
Если у кнопок и т.п. тоже надо, то вроде можно без наследников
https://stackoverflow.com/a/1318308/964478
Control.PreviewKeyDown Event (System.Windows.Forms) | Microsoft Docs

ну и, естественно, чтобы при нажатии клавиш фокус с формы никуда не уходил, иначе

уже активен

Так он и не уходит если они в IsInputKey формы.

уходит

Выше показано, что нет.
Так что либо нет KeyPreview, либо что-то другое странное в коде.