Console ReadKey async or callback?

匿名 (未验证) 提交于 2019-12-03 08:54:24

问题:

I am trying to do a press Q to quit thing in the console window. I dont like my current implementation. Is there a way i can async or use a callback to get keys from the console?

回答1:

You can call Console.ReadKey() from another thread, so that it doesn't block your main thread. (You can use the .Net 4 Task or the old Thread to start the new thread.)

class Program {     static volatile bool exit = false;      static void Main()     {         Task.Factory.StartNew(() =>             {                 while (Console.ReadKey().Key != ConsoleKey.Q) ;                 exit = true;             });          while (!exit)         {             // Do stuff         }     } } 


回答2:

You can use the KeyAvailable property (Framework 2.0) :

if (System.Console.KeyAvailable) {    ConsoleKeyInfo key = System.Console.ReadKey(true);//true don't print char on console    if (key.Key == ConsoleKey.Q)    {        //Do something    } } 


回答3:

I didn't find any of the exisiting answers entirely satisfactory so I wrote my own, to work with TAP and .Net 4.5.

/// <summary> /// Obtains the next character or function key pressed by the user /// asynchronously. The pressed key is displayed in the console window. /// </summary> /// <param name="cancellationToken"> /// The cancellation token that can be used to cancel the read. /// </param> /// <param name="responsiveness"> /// The number of milliseconds to wait between polling the /// <see cref="Console.KeyAvailable"/> property. /// </param> /// <returns>Information describing what key was pressed.</returns> /// <exception cref="TaskCanceledException"> /// Thrown when the read is cancelled by the user input (Ctrl+C etc.) /// or when cancellation is signalled via /// the passed <paramred name="cancellationToken"/>. /// </exception> public static async Task<ConsoleKeyInfo> ReadKeyAsync(     CancellationToken cancellationToken,     int responsiveness = 100) {     var cancelPressed = false;     var cancelWatcher = new ConsoleCancelEventHandler(         (sender, args) => { cancelPressed = true; });     Console.CancelKeyPress += cancelWatcher;     try     {         while (!cancelPressed && !cancellationToken.IsCancellationRequested)         {             if (Console.KeyAvailable)             {                 return Console.ReadKey();             }              await Task.Delay(                 responsiveness,                 cancellationToken);         }          if (cancelPressed)         {             throw new TaskCanceledException(                 "Readkey canceled by user input.");         }          throw new TaskCanceledException();     }     finally     {         Console.CancelKeyPress -= cancelWatcher;     } } 


回答4:

Taking from all the answers here, this is my version:

public class KeyHandler {     public event EventHandler KeyEvent;      public void WaitForExit()     {         bool exit = false;         do         {             var key = Console.ReadKey(true); //blocks until key event             switch (key.Key)             {                 case ConsoleKey.Q:                     exit = true;                     break;                case ConsoleKey.T:                     // raise a custom event eg: Increase throttle                     break;             }         }         while (!exit);     } }   static void Main(string[] args) {     var worker = new MyEventDrivenClassThatDoesCoolStuffByItself();     worker.Start();      var keyHandler = new KeyHandler();     keyHandler.KeyEvent+= keyHandler_KeyEvent; // modify properties of your worker     keyHandler.WaitForExit(); } 
  • It doesn't require Main to do anything in a loop, allowing it to simply orchestrate between handling keys and manipulating properties of the worker class.
  • Taking the hint from @Hans, the KeyHandler doesn't need to async up a new thread since Console.ReadKey blocks until a key is received.


回答5:

Here is an implementation that I created using KeyAvailable. This keeps a prompt at the bottom of the console window while everything "printed" to the console starts from the top.

public class Program {     private static int consoleLine;     private static int consolePromptLine;     private static bool exit;     static string clearLine = new string(' ', Console.BufferWidth - 1);      public static void Main(string[] args)     {         StringBuilder commandCapture = new StringBuilder(10);         string promptArea = "Command> ";          consolePromptLine = Console.WindowTop + Console.WindowHeight - 1;          ClearLine(consolePromptLine);         Console.Write(promptArea);          while (!exit)         {             // Do other stuff              // Process input             if (Console.KeyAvailable)             {                 var character = Console.ReadKey(true);                  if (character.Key == ConsoleKey.Enter)                 {                     if (commandCapture.Length != 0)                     {                         ProcessCommand(commandCapture.ToString());                         commandCapture.Clear();                         ClearLine(consolePromptLine);                         Console.Write(promptArea);                     }                 }                 else                 {                     if (character.Key == ConsoleKey.Backspace)                     {                         if (commandCapture.Length != 0)                         {                             commandCapture.Remove(commandCapture.Length - 1, 1);                             ClearLine(consolePromptLine);                             Console.Write(promptArea);                             Console.Write(commandCapture.ToString());                         }                     }                     else                     {                         commandCapture.Append(character.KeyChar);                         Console.SetCursorPosition(0, consolePromptLine);                         Console.Write(promptArea);                         Console.Write(commandCapture.ToString());                     }                 }             }         }      }      private static void ProcessCommand(string command)     {         if (command == "start")         {             Task<string> testTask = new Task<string>(() => { System.Threading.Thread.Sleep(4000); return "Test Complete"; });              testTask.ContinueWith((t) => { Print(t.Result); }, TaskContinuationOptions.ExecuteSynchronously);             testTask.Start();         }         else if (command == "quit")         {             exit = true;         }          Print(command);         consolePromptLine = Console.WindowTop + Console.WindowHeight - 1;     }      public static void Print(string text)     {         ClearLine(consoleLine);         Console.WriteLine(text);         consoleLine = Console.CursorTop;     }      public static void ClearLine(int line)     {         Console.SetCursorPosition(0, line);         Console.Write(clearLine);         Console.SetCursorPosition(0, line);     } } 


回答6:

Here is how I made it:

// Comments language: pt-BR // Aguarda key no console private static async Task<ConsoleKey> WaitConsoleKey ( ) {     try {         // Prepara retorno         ConsoleKey key = default;         // Aguarda uma tecla ser pressionada         await Task.Run ( ( ) => key = Console.ReadKey ( true ).Key );         // Retorna a tecla         return key;     }     catch ( Exception ex ) {         throw ex;     } } 


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!