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

Ну вот так можно сделать.

 InitializeComponent();

            foreach (Control control in Controls)
            {
                control.PreviewKeyDown += (ox, ex) =>
                {
                    if (ex.KeyData == Keys.Up || ex.KeyData == Keys.Down)
                    {
                        ex.IsInputKey = true;                       
                    }
                };
            }

Это если надо чтобы при любом фокусе. А тут задача только когда нет фокуса, так что IsInputKey или PreviewKeyDown только у формы нужен.

нет фокуса на чем?

На элементах формы.
В коде выше кликом по форме снимается.

Так вроде при щелчке на форму итак снимается без дополнительных действий.

Неа


namespace WindowsFormsApp1
{
    partial class Form1
    {
        /// <summary>
        /// Обязательная переменная конструктора.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// Освободить все используемые ресурсы.
        /// </summary>
        /// <param name="disposing">истинно, если управляемый ресурс должен быть удален; иначе ложно.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Код, автоматически созданный конструктором форм Windows

        /// <summary>
        /// Требуемый метод для поддержки конструктора — не изменяйте 
        /// содержимое этого метода с помощью редактора кода.
        /// </summary>
        private void InitializeComponent()
        {
            this.button1 = new System.Windows.Forms.Button();
            this.listBox1 = new System.Windows.Forms.ListBox();
            this.SuspendLayout();
            // 
            // button1
            // 
            this.button1.Location = new System.Drawing.Point(202, 391);
            this.button1.Name = "button1";
            this.button1.Size = new System.Drawing.Size(75, 23);
            this.button1.TabIndex = 0;
            this.button1.Text = "button1";
            this.button1.UseVisualStyleBackColor = true;
            // 
            // listBox1
            // 
            this.listBox1.FormattingEnabled = true;
            this.listBox1.Location = new System.Drawing.Point(403, 126);
            this.listBox1.Name = "listBox1";
            this.listBox1.Size = new System.Drawing.Size(154, 199);
            this.listBox1.TabIndex = 1;
            // 
            // Form1
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(800, 450);
            this.Controls.Add(this.listBox1);
            this.Controls.Add(this.button1);
            this.KeyPreview = true;
            this.Name = "Form1";
            this.Text = "Form1";
            this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.Form1_KeyDown);
            this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.Form1_MouseDown);
            this.ResumeLayout(false);

        }

        #endregion

        private System.Windows.Forms.Button button1;
        private System.Windows.Forms.ListBox listBox1;
    }
}

форма:

using System;
using System.Diagnostics;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        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 || e.KeyCode == Keys.Down || e.KeyCode == Keys.Left || e.KeyCode == Keys.Right)
            {
                Debug.WriteLine("arrow");

                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);
        }

    }
}

выхлоп:

Space
ActiveControl = 
Space
ActiveControl = 
Up
ActiveControl = 
arrow
"WindowsFormsApp1.exe" (CLR v4.0.30319: WindowsFormsApp1.exe). Загружено "C:\Windows\Microsoft.Net\assembly\GAC_MSIL\mscorlib.resources\v4.0_4.0.0.0_ru_b77a5c561934e089\mscorlib.resources.dll". Сборка модуля выполнена без символов.
Down
ActiveControl = System.Windows.Forms.ListBox
Down
ActiveControl = System.Windows.Forms.ListBox
Down
ActiveControl = System.Windows.Forms.ListBox
Left
ActiveControl = System.Windows.Forms.ListBox

Так в IsInputKey нет же остальных стрелок.

Вообще стрелками обычно не особо удобно управлять, надо переключаться с мышки и обратно, а на ноутах часто еще и с Fn. Лучше WASD.

а, ещё и туда надо было? Чёт я совсем тупанул.

Ну это уже формальности. Я не игру пишу.

не понял :thinking:

Это зависит от расположения мышки и клавиатуры, но обычно ж когда одна рука на мышке, то вторая в левой части клавиатуры, а к правой части надо или как-то неудобно тянуться, или убирать вторую руку с мышки. Особенно если стол с полочкой для клавиатуры.
Потому в играх и WASD.
Ну а на клавиатурах без стрелок две руки, чтоб зажимать Fn.
И сейчас и не для ноутов некоторые берут урезанные клавиатуры, чтоб занимало меньше место.

А, понял. Но тут одновременно мышь и клаву не надо.

Это не игра :slightly_smiling_face:

Добавил на форму CheckBox со свойством Appearance = Button.
Теперь при нажатии вверх влево вниз фокус скачет на чекбокс и обратно на форму. А при нажатии вправо - никуда не скачет :dizzy_face:
Помогает только такой финт ушами:


        private void checkBoxTimer_Enter(object sender, EventArgs e)
        {
            ActiveControl = null;
            Focus();
        }

Ничего не скачет.

        Dictionary<Keys, string> keyTextMap = new Dictionary<Keys, string>
        {
            { Keys.Up, "⬆️" },
            { Keys.Right, "➡️" },
            { Keys.Down, "⬇️" },
            { Keys.Left, "⬅️" },
        };

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

            if (keyTextMap.ContainsKey(e.KeyCode))
            {
                Debug.WriteLine($"Handled {keyTextMap[e.KeyCode]}");

                e.Handled = true;
            }
        }

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

        Keys[] additionalInputKeys = new[] { Keys.Up, Keys.Right, Keys.Down, Keys.Left };

        protected override bool IsInputKey(Keys keyData)
        {
            return additionalInputKeys.Contains(keyData) || base.IsInputKey(keyData);
        }

Peek 2021-07-01 15-36

Обнаружил еще вот что. Если в Load сделать WindowState = FormWindowState.Maximized;, то при запуске форма не приобретает фокус.

        private void Form1_Activated(object sender, EventArgs e)
        {
            ActiveControl = null;
            Focus();
        }

Вместо этого фокус ставится на кнопку.
А если сделать так:


        private void btnPrevImage_Enter(object sender, EventArgs e)
        {
            ActiveControl = null;
            Focus();
        }

то всё нормально.

Не вижу никакой разницы. Всегда в фокусе первый по таб индексу элемент.

и при этом, это

        private void Form1_Activated(object sender, EventArgs e)
        {
            ActiveControl = null;
            Focus();
        }

не ставит фокус на форму

хм, да, странно.
Можно попробовать сообщить о баге ) Visual Studio Feedback

Два раза тему перечитал но так и не понял что нужно сделать.

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

А вот что значит фокус на форму, это как?

Убрать фокус с элементов, чтоб после этого при нажатии кнопок клавиатуры они не отправлялись в поля ввода и т.д.

В браузере например обычно ж тоже примерно так на большинстве страниц: после открытия ничего нет в фокусе, и пробел будет скролить страницу вместо отправки символа в поле ввода или нажатия кнопки. При нажатии в пустое место и т.п. фокус снимается.

Так наверное не получится.
Например контейнеры Form и Panel передают фокус на свой первый дочерний элемент, если явно не указан другой элемент.

P. S.
Можно попробовать убить фокус, как сделали здесь. Но не ручаюсь за эффективность метода, не проверял.

Пример кода по ссылке
public class ViewOnlyTextBox : System.Windows.Forms.TextBox {
    // constants for the message sending
    const int WM_SETFOCUS = 0x0007;
    const int WM_KILLFOCUS = 0x0008;

    protected override void WndProc(ref Message m) {
        if(m.Msg == WM_SETFOCUS) m.Msg = WM_KILLFOCUS;

        base.WndProc (ref m);
    }
}

Так работает же (возможно даже вторая строка не нужна) Управление формой с клавиатуры - #14 от пользователя AlexP

Ну кроме странной проблемы выше про несрабатывание в первом Activated когда WindowState = FormWindowState.Maximized;