2010-12-04 20 views
8

Tengo una aplicación de consola simple escrita en C#. Quiero poder detectar las pulsaciones de las teclas de flecha para poder permitir que el usuario dirija. ¿Cómo puedo detectar eventos de keydown/keyup con una aplicación de consola?Entrada de la tecla de flecha C# para una aplicación de consola

Todo mi Google ha llevado a información sobre Windows Forms. No tengo una GUI. Esta es una aplicación de consola (para controlar un robot sobre un puerto serie).

tengo funciones escritas para manejar estos eventos, pero no tengo ni idea de cómo registrarse para recibir realmente los eventos:

private void myKeyDown(object sender, KeyEventArgs e) 
    { 
     switch (e.KeyCode) 
     { 
      case Keys.Left: 
       ... 
      case Keys.Right: 
       ... 
      case Keys.Up: 
       ... 
     } 
    } 

    private void myKeyUp(object sender, KeyEventArgs e) 
    { 
     ... pretty much the same as myKeyDown 
    } 

Esta es probablemente una pregunta muy básica, pero estoy bastante nuevo en C# , y nunca antes tuve la necesidad de obtener este tipo de información.

Actualización: Muchos están sugiriendo que use System.Console.ReadKey(true).Key. Esto no ayudará. Necesito saber el momento en que se mantiene presionada una tecla, cuando se lanza, con soporte para que se mantengan presionadas varias teclas simultáneamente. Además, ReadKey es una llamada de bloqueo, lo que significa que el programa se detendrá y esperará a que se presione una tecla.

Actualización: Parece que la única forma viable de hacerlo es usar Windows Forms. Esto es molesto, ya que no puedo usarlo en un sistema sin cabeza. Requerir una GUI de formulario para recibir entrada de teclado es ... estúpido.

Pero de todos modos, para la posteridad, aquí está mi solución. He creado un nuevo proyecto en mi Formulario .sln:

private void Form1_Load(object sender, EventArgs e) 
    { 
     try 
     { 
      this.KeyDown += new KeyEventHandler(Form1_KeyDown); 
      this.KeyUp += new KeyEventHandler(Form1_KeyUp); 
     } 
     catch (Exception exc) 
     { 
      ... 
     } 
    } 

    void Form1_KeyDown(object sender, KeyEventArgs e) 
    { 
     switch (e.KeyCode) 
     { 
      // handle up/down/left/right 
      case Keys.Up: 
      case Keys.Left: 
      case Keys.Right: 
      case Keys.Down: 
      default: return; // ignore other keys 
     } 
    } 

    private void Form1_KeyUp(object sender, KeyEventArgs e) 
    { 
     // undo what was done by KeyDown 
    } 

Tenga en cuenta que si se mantiene presionada una tecla, KeyDown será llamado en numerosas ocasiones, y KeyUp es llamado solo una vez (cuando lo suelte). Por lo tanto, debe manejar las repetidas llamadas KeyDown con elegancia.

+0

Puede llamar a ReadKey en un hilo diferente, consulte la sugerencia de mi hilo giratorio. Aunque eso no aborda las cosas de keyup/keydown. – Rushyo

Respuesta

14

Un poco tarde ahora, pero aquí le mostramos cómo acceder al estado del teclado en una aplicación de consola.

Tenga en cuenta que no todo es código administrado, ya que requiere GetKeyState para ser importado de User32.dll.

/// <summary> 
/// Codes representing keyboard keys. 
/// </summary> 
/// <remarks> 
/// Key code documentation: 
/// http://msdn.microsoft.com/en-us/library/dd375731%28v=VS.85%29.aspx 
/// </remarks> 
internal enum KeyCode : int 
{ 
    /// <summary> 
    /// The left arrow key. 
    /// </summary> 
    Left = 0x25, 

    /// <summary> 
    /// The up arrow key. 
    /// </summary> 
    Up, 

    /// <summary> 
    /// The right arrow key. 
    /// </summary> 
    Right, 

    /// <summary> 
    /// The down arrow key. 
    /// </summary> 
    Down 
} 

/// <summary> 
/// Provides keyboard access. 
/// </summary> 
internal static class NativeKeyboard 
{ 
    /// <summary> 
    /// A positional bit flag indicating the part of a key state denoting 
    /// key pressed. 
    /// </summary> 
    private const int KeyPressed = 0x8000; 

    /// <summary> 
    /// Returns a value indicating if a given key is pressed. 
    /// </summary> 
    /// <param name="key">The key to check.</param> 
    /// <returns> 
    /// <c>true</c> if the key is pressed, otherwise <c>false</c>. 
    /// </returns> 
    public static bool IsKeyDown(KeyCode key) 
    { 
     return (GetKeyState((int)key) & KeyPressed) != 0; 
    } 

    /// <summary> 
    /// Gets the key state of a key. 
    /// </summary> 
    /// <param name="key">Virtuak-key code for key.</param> 
    /// <returns>The state of the key.</returns> 
    [System.Runtime.InteropServices.DllImport("user32.dll")] 
    private static extern short GetKeyState(int key); 
} 
+0

Interesante. Esto requeriría sondear el teclado por mi cuenta, lo que significa que no puedo simplemente tener una aplicación basada en eventos. Pero es lo suficientemente simple de hacer. – Tim

+2

Sí, las aplicaciones de la consola normalmente no tienen un bucle de mensajes, pero no hay nada que lo impida, y luego el resto de su aplicación se maneja por evento. – Ergwun

+0

¡Muy útil, +1 para esto! – teodron

4
var isUp = Console.ReadKey().Key == ConsoleKey.UpArrow; 

o otro ejemplo, sólo para su caso:

while (true) 
{ 
    var ch = Console.ReadKey(false).Key; 
    switch(ch) 
    { 
     case ConsoleKey.Escape: 
      ShutdownRobot(); 
      return; 
     case ConsoleKey.UpArrow: 
      MoveRobotUp(); 
      break; 
     case ConsoleKey.DownArrow: 
      MoveRobotDown(); 
      break; 
    } 
} 
0

código de ejemplo:

 ConsoleKeyInfo kb = Console.ReadKey(); 
     if (kb.Key == ConsoleKey.LeftArrow) 
       Console.WriteLine("Left Arrow pressed"); 
2
System.Console.ReadKey(true).Key == ConsoleKey.UpArrow 

Se puede poner que en un giro, algo así como:

while(Running) 
{ 
    DoStuff(); 
    System.Console.ReadKey(true).Key == ConsoleKey.UpArrow 
    Thread.Sleep(1) 
} 
+0

¿Qué ocurre si presiona una tecla o mantiene presionadas varias teclas? No solo quiero presionar la tecla más reciente: quiero manejar varias teclas presionadas (o no presionar ninguna tecla). – Tim

+0

Luego necesita manejar los mensajes del teclado directamente. La forma más fácil de hacerlo es usar DirectInput en Windows. – Rushyo

+0

Si debe hacerlo manualmente, las funciones de Win32 relevantes son GetConsoleWindow y SetWindowsHookEx. Enganche el evento 'WH_CALLWNDPROC' de la consola para atrapar los mensajes de Windows sin formato. Una consola no obtiene mucha de la abstracción que tiene Windows Form, aunque puede tener la funcionalidad que está buscando, el hecho de que nadie haya sugerido que implica para mí que este es su siguiente paso lógico. – Rushyo

0

puede hacerlo

bool keyWasPressed = false; 
if (consolekey.avalable) 
{ 
keyvar = console.readkey(true); 
keyWasPressed = true; 
} 
if(keyWasPressed) 
{ 
//enter you code here using keyvar 
} 
else 
{ 
//the commands that happen if you don't press anything 
} 
0

Tengo el mismo problema que he encontrado y, aquí, un interesante post utilizando tareas. El post original se puede encontrar aquí: C# Console Application - How do I always read input from the console?

I tienen para emular una salida PWM a través de una frambuesa GPIO (usando mono C#) para probar una luz de fondo de LCD. Con dos teclas simples, quería cambiar el ciclo de trabajo (arriba/abajo) y una tecla adicional para detener el programa.

He intentado esto (variables):

static ConsoleKeyInfo key = new ConsoleKeyInfo(); 
static int counter = 0; 
static int duty = 5; // Starts in 50% 

programa principal:

static void Main(string[] args) 
{ 
// cancellation by keyboard string 
    CancellationTokenSource cts = new CancellationTokenSource(); 
    // thread that listens for keyboard input 
    var kbTask = Task.Run(() => 
    { 
     while (true) 
     { 
      key = Console.ReadKey(true); 
      if (key.KeyChar == 'x' || key.KeyChar == 'X') 
      { 
       cts.Cancel(); 
       break; 
      } 
      else if (key.KeyChar == 'W' || key.KeyChar == 'w') 
      { 
       if (duty < 10) 
        duty++; 
       //Console.WriteLine("\tIncrementa Ciclo"); 
       //mainAppState = StateMachineMainApp.State.TIMER; 
       //break; 
      } 
      else if (key.KeyChar == 'S' || key.KeyChar == 's') 
      { 
       if (duty > 0) 
        duty--; 
       //Console.WriteLine("\tDecrementa Ciclo"); 
       //mainAppState = StateMachineMainApp.State.TIMER; 
       // break; 
      } 
     } 
    }); 

    // thread that performs main work 
    Task.Run(() => DoWork(), cts.Token); 

    string OsVersion = Environment.OSVersion.ToString(); 
    Console.WriteLine("Sistema operativo: {0}", OsVersion); 
    Console.WriteLine("Menú de Progama:"); 
    Console.WriteLine(" W. Aumentar ciclo útil"); 
    Console.WriteLine(" S. Disminuir ciclo útil"); 
    Console.WriteLine(" X. Salir del programa"); 

    Console.WriteLine(); 
    // keep Console running until cancellation token is invoked 
    kbTask.Wait(); 
} 

static void DoWork() 
{ 
    while (true) 
    { 
     Thread.Sleep(50); 
     if (counter < 10) 
     { 
      if (counter < duty) 
       Console.Write("─"); 
       //Console.WriteLine(counter + " - ON"); 
      else 
       Console.Write("_"); 
       //Console.WriteLine(counter + " - OFF"); 
      counter++; 
     } 
     else 
     { 
      counter = 0; 
     } 
    } 
} 

Cuando se necesita para incrementar el ciclo de trabajo, pulsando la tecla 'W' hace que la tarea principal cambia el deber variable de ciclo (trabajo); lo mismo con la tecla 'S' para disminuir. El programa termina cuando se presiona la tecla 'X'.

Cuestiones relacionadas