Melhor maneira de implementar atalhos de teclado em um aplicativo Windows Forms?

280

Estou procurando a melhor maneira de implementar atalhos de teclado comuns do Windows (por exemplo Ctrl+ F, Ctrl+ N) no meu aplicativo Windows Forms em C #.

O aplicativo possui um formulário principal que hospeda muitos formulários filhos (um de cada vez). Quando um usuário pressiona Ctrl+ F, eu gostaria de mostrar um formulário de pesquisa personalizado. O formulário de pesquisa dependeria do atual formulário filho aberto no aplicativo.

Eu estava pensando em usar algo assim no evento ChildForm_KeyDown :

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

Mas isso não funciona. O evento nem dispara quando você pressiona uma tecla. Qual é a solução?

Rockcoder
fonte
É realmente estranho que o Winforms não pareça ter uma funcionalidade específica para isso, como a API nativa do Windows.
Stewart

Respostas:

460

Você provavelmente esqueceu de definir a propriedade KeyPreview do formulário como True. A substituição do método ProcessCmdKey () é a solução genérica:

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
fonte
Isso funciona bem, mas só o detecta se o formulário é a janela ativa. Alguém sabe como vinculá-lo para que em qualquer visualização da janela seja possível?
Gaʀʀʏ
Em algumas situações KeyPreviewé bastante indispensável. Por exemplo, ao pressionar um atalho, é necessário suprimir a entrada, mas mesmo assim permitir que um MenuStripevento separado seja acionado. ProcessCmdKeyEssa abordagem forçaria a duplicação da lógica de acionamento de eventos.
Saul
1
Hmm, não, o manipulador de eventos KeyDown do formulário é bastante distinto do manipulador de eventos Click de um item de menu. Você ainda faz exatamente da mesma maneira, ou chame o método manipulador de eventos diretamente (não precisa ser chamado exclusivamente por um evento) ou refatorar a lógica comum em um método separado.
Hans Passant
14
"O que o Ctrl + F?" LOL
kchad 19/05/19
73

No seu formulário principal

  1. Defina KeyPreviewcomo True
  2. Adicione o manipulador de eventos KeyDown com o seguinte código

    private void MainForm_KeyDown(object sender, KeyEventArgs e)
    {
        if (e.Control && e.KeyCode == Keys.N)
        {
            SearchForm searchForm = new SearchForm();
            searchForm.Show();
        }
    }
Almir
fonte
4
+ para, Definir KeyPreview como True .. estava faltando isso #
Usman Younas 30/10
20

A melhor maneira é usar a mnemônica de menus, ou seja, ter entradas de menu em seu formulário principal que recebam o atalho de teclado que você deseja. Todo o resto é tratado internamente e tudo o que você precisa fazer é implementar a ação apropriada que é executada no Clickmanipulador de eventos dessa entrada de menu.

Konrad Rudolph
fonte
1
O problema é que o formulário principal não usa menus comuns do Windows. Ele usa o painel de navegação personalizado usado para mostrar formulários filhos. Os formulários de pesquisa são chamados clicando no ToolStripButton no formulário filho.
Rockcoder
menu mnemonicsamostra real?
Kiquenet 6/02/19
1
@Kiquenet docs.microsoft.com/en-us/dotnet/api/…
Konrad Rudolph
1
Mnemônicos são um conceito distinto das teclas de atalho, e existem diferenças importantes em como elas operam. Em resumo, mnemônicos são os caracteres sublinhados usados ​​para acessar menus, comandos de menu e controles de diálogo usando o teclado. OTOH, teclas de atalho são uma maneira de acessar comandos sem passar pelos menus e, além disso, podem ser pressionamentos de tecla arbitrários como Ctrl + F ou F5, não restritos a teclas de caracteres.
Stewart
1
@ Stewart Isso está correto! Minha resposta não estava tentando sugerir que todos os atalhos de teclado são mnemônicos, apenas que os mnemônicos de menu são a maneira mais fácil de obter essa funcionalidade. Infelizmente, como esclarece o comentário da Rockcoder, esta solução não parece apropriada aqui. Eu ainda o recomendo como a solução preferível, sempre que possível. Para uma solução geral, a resposta aceita por Hans é o caminho a seguir.
quer tocar hoje
11

Você pode até tentar este exemplo:

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
fonte
8

Se você tiver um menu, alterar a ShortcutKeyspropriedade do ToolStripMenuItemdeve fazer o truque.

Caso contrário, você pode criar um e definir sua visiblepropriedade como false.

Corin Blaikie
fonte
Não, eu não tenho um menu. O ToolStripButton para pesquisa está realmente localizado no controle BindingNavigator, portanto, adicionar um menu provavelmente não é uma opção.
Rockcoder 30/12/08
6

No formulário principal, você deve:

  • Certifique-se de definir KeyPreview como true (TRUE por padrão)
  • Adicione MainForm_KeyDown (..) - pelo qual você pode definir aqui os atalhos que desejar.

Além disso, eu encontrei isso no google e queria compartilhar isso com aqueles que ainda estão procurando respostas. (para global)

Eu acho que você deve estar usando 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
    }
}

Leia mais em http://www.fluxbytes.com/csharp/how-to-register-a-global-hotkey-for-your-application-in-c/

Juran
fonte
4

A resposta de Hans poderia ser um pouco mais fácil para alguém novo, então aqui está minha versão.

Você não precisa se enganar KeyPreview, deixe-o definido como false. Para usar o código abaixo, cole-o abaixo do seu form1_loade execute-o F5para que ele funcione:

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
fonte
7
Primeiro, substituir ProcessCmdKey é a maneira recomendada de lidar com um pressionamento de tecla destinado a executar alguma operação semelhante a um comando, que é o que o OP estava perguntando. Segundo, você não entendeu o comentário de Hans sobre como configurar KeyPreview como true; ele estava apenas dizendo ao OP por que sua técnica não estava funcionando. Definir KeyPreview como true não é necessário para usar ProcessCmdKey.
RenniePet
2

No WinForm, sempre podemos obter o status da Chave de Controle:

bool IsCtrlPressed = (Control.ModifierKeys & Keys.Control) != 0;
sk
fonte