Console ReadKey async or callback?

前端 未结 6 842
萌比男神i
萌比男神i 2021-01-04 14:04

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 con

相关标签:
6条回答
  • 2021-01-04 14:28

    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);
        }
    }
    
    0 讨论(0)
  • 2021-01-04 14:36

    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
            }
        }
    }
    
    0 讨论(0)
  • 2021-01-04 14:41

    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.
    0 讨论(0)
  • 2021-01-04 14:43

    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
       }
    }
    
    0 讨论(0)
  • 2021-01-04 14:46

    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;
        }
    }
    
    0 讨论(0)
  • 2021-01-04 14:49

    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;
        }
    }
    
    0 讨论(0)
提交回复
热议问题