2010-08-01 5 views
4

Я пытаюсь сделать нажатие Q, чтобы выйти из окна консоли. Мне не нравится моя текущая реализация. Есть ли способ, которым я могу асинхронно или использовать обратный вызов для получения ключей из консоли?Консоль ReadKey async или обратный вызов?

+1

Показать нам codez – Oded

ответ

12

Вы можете позвонить Console.ReadKey() из другого потока, чтобы он не блокировал основной поток. (Вы можете использовать .Net 4 Task или старый 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 
     } 
    } 
} 
+0

Thats actially, что я делаю (ну, ReadKey часть на DIF нити). Эта фабрика задач выглядит круто. Проблема с моим кодом прямо сейчас .ReadKey, кажется, не возвращается, если я не заставляю окно фокусироваться. Поэтому почему мне не нравится мое решение. Когда основная нить заканчивается, другой поток все еще заблокирован. Ваше решение выглядит круто, возможно, потоки задач будут умирать, когда основной будет. В этом случае это должно работать, однако ATM. Это приложение только для 3.5. – 2010-08-01 15:41:31

+4

Избегайте потока с помощью Console.KeyAvailable. –

+1

Если вы хотите, чтобы «Thread» остановился при завершении основного потока, просто установите 'Thread.IsBackground' в' true'. – svick

4

Вы можете использовать KeyAvailable свойство (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 
    } 
} 
4

я не нашел любой из существующих ответов вполне удовлетворительный, поэтому я написал свои собственные, чтобы работать с TAP и .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; 
    } 
} 
0

Забрав из всех ответов здесь, это моя версия:

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(); 
} 
  • Он не требует Main, чтобы сделать что-нибудь в цикле, что позволяет просто организовать между клавишами обработки и манипулируя свойствами рабочего класса.
  • Принимая подсказку с @Hans, KeyHandler не нуждается в асинхронизации нового потока, поскольку Console.ReadKey блокируется до тех пор, пока не будет получен ключ.
0

Вот реализация, которую я создал с помощью KeyAvailable. Это приводит к появлению подсказки в нижней части окна консоли, а все, что напечатано на консоли, начинается сверху.

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); 
    } 
} 
Смежные вопросы