Warten Sie in der .NET-Konsolen-App auf Tastendruck

224

Wie kann ich meine Konsolenanwendung so lange ausführen, bis ein Tastendruck (wie Escgedrückt wird?)

Ich gehe davon aus, dass es um eine while-Schleife gewickelt ist. Ich mag es nicht, ReadKeyweil es den Betrieb blockiert und nach einer Taste fragt, anstatt einfach fortzufahren und auf den Tastendruck zu warten.

Wie kann das gemacht werden?

karlstackoverflow
quelle

Antworten:

343

Verwenden Console.KeyAvailableSie diese Option, damit Sie nur anrufen, ReadKeywenn Sie wissen, dass sie nicht blockiert:

Console.WriteLine("Press ESC to stop");
do {
    while (! Console.KeyAvailable) {
        // Do something
   }       
} while (Console.ReadKey(true).Key != ConsoleKey.Escape);
Jeff Sternal
quelle
6
Wenn Sie dies jedoch anstelle von readLine () tun, verlieren Sie die großartige Funktion des Abrufs des Verlaufs, indem Sie die Aufwärts-Taste drücken.
v.oddou
3
@ v.oddou Nach der letzten Zeile fügen Sie einfach Ihre hinzu Console.ReadLine()und schon sind Sie fertig , nein?
Gaffi
1
Wird diese Schleife nicht viel CPU / RAM verbrauchen? Wenn nicht, wie?
Sofia
78

Sie können Ihren Ansatz leicht ändern - verwenden Console.ReadKey()Sie diese Option, um Ihre App zu stoppen, aber erledigen Sie Ihre Arbeit in einem Hintergrund-Thread:

static void Main(string[] args)
{
    var myWorker = new MyWorker();
    myWorker.DoStuff();
    Console.WriteLine("Press any key to stop...");
    Console.ReadKey();
}

In der myWorker.DoStuff()Funktion würden Sie dann eine andere Funktion in einem Hintergrundthread aufrufen (mit Action<>()oder auf Func<>()einfache Weise) und dann sofort zurückkehren.

Slugster
quelle
60

Der kürzeste Weg:

Console.WriteLine("Press ESC to stop");

while (!(Console.KeyAvailable && Console.ReadKey(true).Key == ConsoleKey.Escape))
{
    // do something
}

Console.ReadKey() ist eine Sperrfunktion, die die Ausführung des Programms stoppt und auf einen Tastendruck wartet, aber dank Überprüfung Console.KeyAvailable ersten wird die whileSchleife nicht blockiert, sondern läuft, bis die Escgedrückt wird.

Yuliia Ashomok
quelle
Das bisher einfachste Beispiel. Danke dir.
Joel
18

Aus dem Videofluch Erstellen von .NET-Konsolenanwendungen in C # von Jason Roberts unter http://www.pluralsight.com

Wir könnten folgen, um mehrere laufende Prozesse zu haben

  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);
        }
    }
Alhpe
quelle
2
Können Sie ein wenig über den Ansatz erklären, den Sie hatten? Ich versuche, dasselbe mit meiner Konsolenanwendung zu tun. Eine Erklärung für den Code wäre großartig.
Nayef Harb
@nayefharb richtet eine Reihe von Aufgaben ein, startet sie und WaitAll wartet, bis alle zurückgekehrt sind. Daher werden die einzelnen Aufgaben nicht zurückgegeben und für immer fortgesetzt, bis eine CancelKeyPress ausgelöst wird.
Wonea
Console.CursorVisible = false;Vermeiden Sie am besten das Blinken des Cursors. Fügen Sie diese Zeile als erste Zeile im static void Main(string[] args)Block hinzu.
Hitesh
12

Hier ist ein Ansatz, mit dem Sie etwas in einem anderen Thread tun und die in einem anderen Thread gedrückte Taste anhören können. Und die Konsole stoppt ihre Verarbeitung, wenn Ihr eigentlicher Prozess endet oder der Benutzer den Prozess durch Drücken der EscTaste beendet.

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;

        }
    }

}
waheed
quelle
5

Wenn Sie Visual Studio verwenden, können Sie im Menü Debuggen "Ohne Debuggen starten" verwenden.

Es wird automatisch geschrieben: "Drücken Sie eine beliebige Taste, um fortzufahren." Wenn Sie die Anwendung abgeschlossen haben, wird die Konsole für Sie geöffnet, bis eine Taste gedrückt wird.

LVBen
quelle
3

Behandlung von Fällen, mit denen einige der anderen Antworten nicht gut umgehen:

  • Reaktionsschnell : direkte Ausführung des Tastendruck-Handhabungscodes; vermeidet die Unklarheiten beim Abrufen oder Blockieren von Verzögerungen
  • Optionalität : Globaler Tastendruck ist Opt-In ; Andernfalls sollte die App normal beendet werden
  • Trennung von Bedenken : weniger invasiver Abhörcode; arbeitet unabhängig vom normalen Konsolen-App-Code.

Viele der Lösungen auf dieser Seite beinhalten das Abrufen Console.KeyAvailableoder Blockieren Console.ReadKey. Zwar Consoleist .NET hier nicht sehr kooperativ, aber Sie können es verwenden Task.Run, um zu einer moderneren AsyncArt des Hörens überzugehen.

Das Hauptproblem, das Sie beachten sollten, ist, dass Ihr Konsolenthread standardmäßig nicht für den AsyncBetrieb eingerichtet ist. Wenn Sie also aus dem unteren Bereich Ihrer mainFunktion Asyncherausfallen , anstatt auf den Abschluss zu warten , werden AppDoman und Prozess beendet . Eine geeignete Möglichkeit, dies zu beheben , besteht darin, Stephen Clearys AsyncContext zu verwenden, um die vollständige AsyncUnterstützung für Ihr Single-Threaded-Konsolenprogramm herzustellen . Aber für einfachere Fälle, wie das Warten auf einen Tastendruck, das Installieren eines vollen Trampolins jedoch übertrieben sein.

Das folgende Beispiel bezieht sich auf ein Konsolenprogramm, das in einer iterativen Batchdatei verwendet wird. In diesem Fall sollte das Programm, wenn es mit seiner Arbeit fertig ist, normalerweise ohne Tastendruck beendet werden. Anschließend erlauben wir einen optionalen Tastendruck, um das Beenden der App zu verhindern . Wir können den Zyklus anhalten, um Dinge zu untersuchen, möglicherweise wieder aufzunehmen, oder die Pause als bekannten "Kontrollpunkt" verwenden, an dem die Batchdatei sauber herausgebrochen werden kann.

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
    }
}
Glenn Slayden
quelle
1

Mit dem folgenden Code können Sie das Drücken der SpaceBar während der Ausführung Ihrer Konsole anhören und eine Pause einlegen, bis eine andere Taste gedrückt wird. Außerdem können Sie auf EscapeKey warten, um die Hauptschleife zu unterbrechen

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;
    }
Iman
quelle
-1

Nach meiner Erfahrung ist in Konsolen-Apps der einfachste Weg, die zuletzt gedrückte Taste zu lesen, wie folgt (Beispiel mit Pfeiltasten):

ConsoleKey readKey = Console.ReadKey ().Key;
if (readKey == ConsoleKey.LeftArrow) {
    <Method1> ();  //Do something
} else if (readKey == ConsoleKey.RightArrow) {
    <Method2> ();  //Do something
}

Ich vermeide Schleifen, schreibe stattdessen den obigen Code in eine Methode und rufe ihn am Ende von "Methode1" und "Methode2" auf. Nach der Ausführung von "Methode1" oder "Methode2" wird Console.ReadKey () Der Schlüssel ist bereit, die Schlüssel erneut zu lesen.

Dhemmlerf
quelle