¿La mejor manera de implementar métodos abreviados de teclado en una aplicación de formularios Windows Forms?

280

Estoy en busca de una mejor manera de implementar los accesos directos comunes del teclado de Windows (por ejemplo, Ctrl+ F, Ctrl+ N) en mi Windows Forms aplicación en C #.

La aplicación tiene un formulario principal que aloja muchos formularios secundarios (uno a la vez). Cuando un usuario presiona Ctrl+ F, me gustaría mostrar un formulario de búsqueda personalizado. El formulario de búsqueda dependerá del formulario secundario abierto actual en la aplicación.

Estaba pensando en usar algo como esto en el evento ChildForm_KeyDown :

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

Pero esto no funciona. El evento ni siquiera se dispara cuando presionas una tecla. ¿Cuál es la solución?

Rockcoder
fuente
Es realmente extraño que Winforms no parezca tener una funcionalidad específica para esto como lo hace la API nativa de Windows.
Stewart

Respuestas:

460

Probablemente olvidó establecer la propiedad KeyPreview del formulario en True. Anular el método ProcessCmdKey () es la solución 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
fuente
Esto funciona bien, pero solo lo detecta si el formulario es la ventana activa. ¿Alguien sabe cómo vincularlo, así que en cualquier vista de ventana es posible?
Gaʀʀʏ
En algunas situaciones KeyPreviewes bastante indispensable. Por ejemplo, al presionar un atajo es necesario suprimir la entrada pero, sin embargo, permitir que se active un MenuStripevento separado . ProcessCmdKeyenfoque forzaría la duplicación de la lógica de disparo de eventos.
Saul
1
Hmm, no, el controlador de eventos KeyDown del formulario es bastante distinto del controlador de eventos Click de un elemento de menú. Todavía hace exactamente lo mismo, ya sea llamando directamente al método del controlador de eventos (no es necesario que sea llamado exclusivamente por un evento) o refactorizando la lógica común en un método separado.
Hans Passant
14
"¿Qué es Ctrl + F?" LOL
kchad
73

En su formulario principal

  1. Establecer KeyPreviewen verdadero
  2. Agregue el controlador de eventos KeyDown con el siguiente código

    private void MainForm_KeyDown(object sender, KeyEventArgs e)
    {
        if (e.Control && e.KeyCode == Keys.N)
        {
            SearchForm searchForm = new SearchForm();
            searchForm.Show();
        }
    }
Almir
fuente
44
+ para, Establecer KeyPreview en True ... le faltaba eso
Usman Younas
20

La mejor manera es usar la mnemotecnia del menú, es decir, tener entradas de menú en su formulario principal que tengan asignado el método abreviado de teclado que desee. Luego, todo lo demás se maneja internamente y todo lo que tiene que hacer es implementar la acción adecuada que se ejecuta en el Clickcontrolador de eventos de esa entrada del menú.

Konrad Rudolph
fuente
1
El problema es que el formulario principal no usa menús comunes de Windows. Utiliza un panel de navegación personalizado que se utiliza para mostrar formularios secundarios. Los formularios de búsqueda se invocan haciendo clic en ToolStripButton en el formulario secundario.
Rockcoder
menu mnemonicsmuestra real?
Kiquenet
1
@ Kiquenet docs.microsoft.com/en-us/dotnet/api/…
Konrad Rudolph
1
Los mnemónicos son un concepto distinto de las teclas de acceso directo, y existen diferencias importantes en su funcionamiento. En resumen, los mnemónicos son los caracteres subrayados que se utilizan para acceder a los menús, los comandos de menú y los controles de diálogo utilizando el teclado. OTOH, las teclas de acceso directo son una forma de acceder a los comandos sin pasar por los menús, y además pueden ser pulsaciones de teclas arbitrarias como Ctrl + F o F5, no restringidas a las teclas de caracteres.
Stewart
1
@ Stewart ¡Eso es correcto! Mi respuesta no estaba tratando de implicar que todos los atajos de teclado son mnemónicos, simplemente que los mnemónicos de menú son la forma más fácil de obtener esta funcionalidad. Desafortunadamente, como aclara el comentario de Rockcoder, esta solución no parece apropiada aquí. Todavía lo recomiendo como la solución preferible, siempre que sea posible. Para una solución general, la respuesta aceptada de Hans es el camino a seguir.
Konrad Rudolph
11

Incluso puedes probar este ejemplo:

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

Si tiene un menú, entonces cambie la ShortcutKeyspropiedad deToolStripMenuItem debería hacer el truco.

Si no, puede crear uno y establecer su visiblepropiedad en falso.

Corin Blaikie
fuente
No, no tengo un menú. ToolStripButton for search se encuentra realmente en el control BindingNavigator, por lo que agregar un menú probablemente no sea una opción.
Rockcoder
6

Desde el formulario principal, debe:

  • Asegúrese de establecer KeyPreview en verdadero (VERDADERO por defecto)
  • Agregue MainForm_KeyDown (..), mediante el cual puede establecer aquí los accesos directos que desee.

Además, he encontrado esto en Google y quería compartirlo con aquellos que todavía están buscando respuestas. (para global)

Creo que tienes que 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
    }
}

Lea más esto http://www.fluxbytes.com/csharp/how-to-register-a-global-hotkey-for-your-application-in-c/

Juran
fuente
4

La respuesta de Hans podría ser un poco más fácil para alguien nuevo en esto, así que aquí está mi versión.

No necesita engañar KeyPreview, déjelo configurado false. Para usar el código a continuación, simplemente péguelo debajo de form1_loady ejecute F5para verlo funcionar:

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
fuente
77
Primero, anular ProcessCmdKey es la forma recomendada de manejar una pulsación de tecla destinada a realizar una operación similar a un comando, que es lo que el OP estaba preguntando. En segundo lugar, ha entendido mal el comentario de Hans sobre establecer KeyPreview en verdadero; solo le estaba diciendo al OP por qué su técnica no estaba funcionando. Establecer KeyPreview en verdadero no es necesario para usar ProcessCmdKey.
RenniePet
2

En WinForm, siempre podemos obtener el estado de la clave de control:

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