Лучший способ реализовать сочетания клавиш в приложении Windows Forms?

280

Я ищу лучший способ реализовать общие сочетания клавиш Windows (например, Ctrl+ F, Ctrl+ N) в моем приложении Windows Forms в C #.

Приложение имеет основную форму, которая содержит много дочерних форм (по одной). Когда пользователь нажимает Ctrl+ F, я хотел бы показать пользовательскую форму поиска. Форма поиска будет зависеть от текущей открытой дочерней формы в приложении.

Я думал об использовании чего-то подобного в событии ChildForm_KeyDown :

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

Но это не работает. Событие даже не срабатывает при нажатии клавиши. Каково решение?

Rockcoder
источник
Действительно странно, что Winforms, кажется, не имеют специфической функциональности для этого, как в собственном Windows API.
Стюарт

Ответы:

460

Возможно, вы забыли установить для свойства KeyPreview формы значение True. Переопределение метода ProcessCmdKey () является общим решением:

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);
}
Ганс Пассант
источник
Это работает хорошо, но обнаруживает это только для меня, если форма является активным окном. Кто-нибудь знает, как его связать, чтобы в любом окне было возможно?
Gaʀʀʏ
В некоторых ситуациях KeyPreviewэто совершенно необходимо. Например, при нажатии на ярлык требуется подавить ввод, но, тем не менее, разрешить запуск отдельного MenuStripсобытия. ProcessCmdKeyподход заставил бы дублировать логику запуска событий.
Сол
1
Хм, нет, обработчик события KeyDown формы совершенно отличается от обработчика события Click элемента меню. Вы по-прежнему делаете то же самое, либо вызываете метод обработчика событий напрямую (нет необходимости вызывать его исключительно событием), либо реорганизуете общую логику в отдельный метод.
Ганс Пассант
14
"Что такое Ctrl + F?" LOL
кчад
73

На вашей основной форме

  1. Установите KeyPreviewв True
  2. Добавьте обработчик события KeyDown с помощью следующего кода

    private void MainForm_KeyDown(object sender, KeyEventArgs e)
    {
        if (e.Control && e.KeyCode == Keys.N)
        {
            SearchForm searchForm = new SearchForm();
            searchForm.Show();
        }
    }
    
Альмир
источник
4
+ для, установите KeyPreview в True .. не хватало этого
Усман Юнас
20

Лучший способ - использовать мнемонику меню, то есть иметь пункты меню в основной форме, которым назначены нужные сочетания клавиш. Затем все остальное обрабатывается внутренне, и все, что вам нужно сделать, это реализовать соответствующее действие, которое выполняется в Clickобработчике события этого пункта меню.

Конрад Рудольф
источник
1
Проблема в том, что основная форма не использует общие меню окон. Он использует пользовательскую панель навигации, которая используется для отображения дочерних форм. Формы поиска вызываются нажатием на ToolStripButton дочерней формы.
Rockcoder
menu mnemonicsреальный образец?
Kiquenet
1
@Kiquenet docs.microsoft.com/en-us/dotnet/api/…
Конрад Рудольф,
1
Мнемоника отличается от сочетаний клавиш, и существуют важные различия в том, как они работают. Вкратце, мнемоника - это подчеркнутые символы, используемые для доступа к меню, командам меню и элементам управления диалогов с помощью клавиатуры. OTOH, сочетания клавиш - это способ доступа к командам без прохождения через меню, и, кроме того, они могут быть произвольными нажатиями клавиш, такими как Ctrl + F или F5, не ограничиваясь символьными клавишами.
Стюарт
1
@ Стюарт Это правильно! В моем ответе я не пытался предположить, что все сочетания клавиш являются мнемоникой, а просто то, что меню - это самый простой способ получить эту функциональность. К сожалению, как поясняет комментарий Rockcoder, это решение здесь не кажется подходящим. Я все еще рекомендую это как предпочтительное решение, где это возможно. Для общего решения принятый ответ Ганса - путь вперед.
Конрад Рудольф
11

Вы даже можете попробовать этот пример:

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...
}
Шилпа
источник
8

Если у вас есть меню, то изменение ShortcutKeysсвойства ToolStripMenuItemдолжно помочь.

Если нет, вы можете создать его и установить для его visibleсвойства значение false.

Корин Блэйки
источник
Нет, у меня нет меню. ToolStripButton для поиска фактически находится в элементе управления BindingNavigator, поэтому добавление меню, вероятно, не вариант.
Rockcoder
6

Из основной формы вы должны:

  • Убедитесь , что вы установили KeyPreview к истинным (TRUE , по умолчанию)
  • Добавьте MainForm_KeyDown (..) - с помощью которого вы можете установить здесь любые ярлыки, которые вы хотите.

Кроме того, я нашел это в Google, и я хотел бы поделиться этим с теми, кто все еще ищет ответы. (для глобального)

Я думаю, что вы должны использовать 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
    }
}

Далее читайте это http://www.fluxbytes.com/csharp/how-to-register-a-global-hotkey-for-your-application-in-c/

Юран
источник
4

Кто-то новичок в этом вопросе может немного облегчить ответ Ханса , так что вот моя версия.

Вам не нужно дурачиться KeyPreview, оставьте его установленным на false. Чтобы использовать приведенный ниже код, просто вставьте его под свой form1_loadи запустите, F5чтобы увидеть, как он работает:

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();
    }
}
Абхишек Джа
источник
7
Во-первых, переопределение ProcessCmdKey - это рекомендуемый способ обработки нажатия клавиш, предназначенный для выполнения некоторой командоподобной операции, о которой спрашивал OP. Во-вторых, вы неправильно поняли комментарий Ганса о том, что для KeyPreview задано значение true; он просто рассказывал ОП, почему его техника не работает. Установка KeyPreview в true не требуется для использования ProcessCmdKey.
Ренни Пит
2

В WinForm мы всегда можем получить статус управляющего ключа:

bool IsCtrlPressed = (Control.ModifierKeys & Keys.Control) != 0;
ск
источник