如何继续运行控制台应用程序,直到按键(如Esc被按下?)。
我假设它包裹在while循环中。我不喜欢ReadKey它阻止操作并要求输入按键,而不是继续听按键的声音。
如何才能做到这一点?
如何继续运行控制台应用程序,直到按键(如Esc被按下?)。
我假设它包裹在while循环中。我不喜欢ReadKey它阻止操作并要求输入按键,而不是继续听按键的声音。
如何才能做到这一点?
Answers:
使用,Console.KeyAvailable以便仅ReadKey在您知道它不会阻止时呼叫:
Console.WriteLine("Press ESC to stop");
do {
    while (! Console.KeyAvailable) {
        // Do something
   }       
} while (Console.ReadKey(true).Key != ConsoleKey.Escape);
              Console.ReadLine()就可以了,是吗?
                    最短的方法:
Console.WriteLine("Press ESC to stop");
while (!(Console.KeyAvailable && Console.ReadKey(true).Key == ConsoleKey.Escape))
{
    // do something
}
Console.ReadKey()是一个阻止功能,它停止程序的执行并等待按键,但是由于Console.KeyAvailable首先进行了检查,因此该while循环没有被阻止,而是一直运行到Esc被按下为止。
摘自Jason Roberts在http://www.pluralsight.com上的视频诅咒“用C#构建.NET控制台应用程序”
我们可以按照以下步骤进行多个运行过程
  static void Main(string[] args)
    {
        Console.CancelKeyPress += (sender, e) =>
        {
            Console.WriteLine("Exiting...");
            Environment.Exit(0);
        };
        Console.WriteLine("Press ESC to Exit");
        var taskKeys = new Task(ReadKeys);
        var taskProcessFiles = new Task(ProcessFiles);
        taskKeys.Start();
        taskProcessFiles.Start();
        var tasks = new[] { taskKeys };
        Task.WaitAll(tasks);
    }
    private static void ProcessFiles()
    {
        var files = Enumerable.Range(1, 100).Select(n => "File" + n + ".txt");
        var taskBusy = new Task(BusyIndicator);
        taskBusy.Start();
        foreach (var file in files)
        {
            Thread.Sleep(1000);
            Console.WriteLine("Procesing file {0}", file);
        }
    }
    private static void BusyIndicator()
    {
        var busy = new ConsoleBusyIndicator();
        busy.UpdateProgress();
    }
    private static void ReadKeys()
    {
        ConsoleKeyInfo key = new ConsoleKeyInfo();
        while (!Console.KeyAvailable && key.Key != ConsoleKey.Escape)
        {
            key = Console.ReadKey(true);
            switch (key.Key)
            {
                case ConsoleKey.UpArrow:
                    Console.WriteLine("UpArrow was pressed");
                    break;
                case ConsoleKey.DownArrow:
                    Console.WriteLine("DownArrow was pressed");
                    break;
                case ConsoleKey.RightArrow:
                    Console.WriteLine("RightArrow was pressed");
                    break;
                case ConsoleKey.LeftArrow:
                    Console.WriteLine("LeftArrow was pressed");
                    break;
                case ConsoleKey.Escape:
                    break;
                default:
                    if (Console.CapsLock && Console.NumberLock)
                    {
                        Console.WriteLine(key.KeyChar);
                    }
                    break;
            }
        }
    }
}
internal class ConsoleBusyIndicator
{
    int _currentBusySymbol;
    public char[] BusySymbols { get; set; }
    public ConsoleBusyIndicator()
    {
        BusySymbols = new[] { '|', '/', '-', '\\' };
    }
    public void UpdateProgress()
    {
        while (true)
        {
            Thread.Sleep(100);
            var originalX = Console.CursorLeft;
            var originalY = Console.CursorTop;
            Console.Write(BusySymbols[_currentBusySymbol]);
            _currentBusySymbol++;
            if (_currentBusySymbol == BusySymbols.Length)
            {
                _currentBusySymbol = 0;
            }
            Console.SetCursorPosition(originalX, originalY);
        }
    }
              Console.CursorVisible = false;最好避免光标闪烁故障。将此行添加为static void Main(string[] args)块中的第一行。
                    这是一种让您在其他线程上执行某些操作并开始监听在其他线程中按下的键的方法。当您的实际过程结束或用户通过按键终止该过程时,控制台将停止其处理Esc。
class SplitAnalyser
{
    public static bool stopProcessor = false;
    public static bool Terminate = false;
    static void Main(string[] args)
    {
        Console.ForegroundColor = ConsoleColor.Green;
        Console.WriteLine("Split Analyser starts");
        Console.ForegroundColor = ConsoleColor.Red;
        Console.WriteLine("Press Esc to quit.....");
        Thread MainThread = new Thread(new ThreadStart(startProcess));
        Thread ConsoleKeyListener = new Thread(new ThreadStart(ListerKeyBoardEvent));
        MainThread.Name = "Processor";
        ConsoleKeyListener.Name = "KeyListener";
        MainThread.Start();
        ConsoleKeyListener.Start();
        while (true) 
        {
            if (Terminate)
            {
                Console.WriteLine("Terminating Process...");
                MainThread.Abort();
                ConsoleKeyListener.Abort();
                Thread.Sleep(2000);
                Thread.CurrentThread.Abort();
                return;
            }
            if (stopProcessor)
            {
                Console.WriteLine("Ending Process...");
                MainThread.Abort();
                ConsoleKeyListener.Abort();
                Thread.Sleep(2000);
                Thread.CurrentThread.Abort();
                return;
            }
        } 
    }
    public static void ListerKeyBoardEvent()
    {
        do
        {
            if (Console.ReadKey(true).Key == ConsoleKey.Escape)
            {
                Terminate = true;
            }
        } while (true); 
    }
    public static void startProcess()
    {
        int i = 0;
        while (true)
        {
            if (!stopProcessor && !Terminate)
            {
                Console.ForegroundColor = ConsoleColor.White;
                Console.WriteLine("Processing...." + i++);
                Thread.Sleep(3000);
            }
            if(i==10)
                stopProcessor = true;
        }
    }
}
              处理某些其他答案无法很好解决的情况:
此页面上的许多解决方案都涉及对的轮询Console.KeyAvailable或阻止Console.ReadKey。虽然.NET Console确实在这里并不是很合作,但是您可以使用它Task.Run来走向更现代Async的侦听模式。
要注意的主要问题是,默认情况下,您的控制台线程未设置用于Async操作-意味着,当您退出main函数的底部时Async,AppDoman和进程将结束,而不是等待完成。解决此问题的正确方法是使用Stephen Cleary的AsyncContextAsync在您的单线程控制台程序中建立完全支持。但是对于更简单的情况,例如等待按键,安装完整的蹦床可能会过大。
下面的示例将用于某种迭代批处理文件中的控制台程序。在这种情况下,当程序完成工作后,通常应在不需要按键的情况下退出,然后我们允许进行可选的按键操作以防止应用退出。我们可以暂停循环以检查可能的事情,或者将暂停用作已知的“控制点”,从那里可以清晰地脱离批处理文件。
static void Main(String[] args)
{
    Console.WriteLine("Press any key to prevent exit...");
    var tHold = Task.Run(() => Console.ReadKey(true));
    // ... do your console app activity ...
    if (tHold.IsCompleted)
    {
#if false   // For the 'hold' state, you can simply halt forever...
        Console.WriteLine("Holding.");
        Thread.Sleep(Timeout.Infinite);
#else                            // ...or allow continuing to exit
        while (Console.KeyAvailable)
            Console.ReadKey(true);     // flush/consume any extras
        Console.WriteLine("Holding. Press 'Esc' to exit.");
        while (Console.ReadKey(true).Key != ConsoleKey.Escape)
            ;
#endif
    }
}
              使用下面的代码,您可以在控制台执行的中间监听SpaceBar按下,暂停直到按下另一个键,还可以监听EscapeKey来中断主循环
static ConsoleKeyInfo cki = new ConsoleKeyInfo();
while(true) {
      if (WaitOrBreak()) break;
      //your main code
}
 private static bool WaitOrBreak(){
        if (Console.KeyAvailable) cki = Console.ReadKey(true);
        if (cki.Key == ConsoleKey.Spacebar)
        {
            Console.Write("waiting..");
            while (Console.KeyAvailable == false)
            {
                Thread.Sleep(250);Console.Write(".");
            }
            Console.WriteLine();
            Console.ReadKey(true);
            cki = new ConsoleKeyInfo();
        }
        if (cki.Key == ConsoleKey.Escape) return true;
        return false;
    }
              根据我的经验,在控制台应用程序中,读取最后按下的键的最简单方法如下(带有箭头键的示例):
ConsoleKey readKey = Console.ReadKey ().Key;
if (readKey == ConsoleKey.LeftArrow) {
    <Method1> ();  //Do something
} else if (readKey == ConsoleKey.RightArrow) {
    <Method2> ();  //Do something
}
我使用避免循环,而是在一个方法中编写上面的代码,并在“ Method1”和“ Method2”的末尾调用它,因此,在执行“ Method1”或“ Method2”之后,Console.ReadKey() .Key准备好再次读取密钥。