Dengarkan untuk menekan tombol di aplikasi .NET console

224

Bagaimana saya bisa terus menjalankan aplikasi konsol saya sampai tombol ditekan (suka Escditekan?)

Saya menganggap itu melilit loop sementara. Saya tidak sukaReadKey karena memblokir operasi dan meminta kunci, daripada hanya melanjutkan dan mendengarkan tekan tombol.

Bagaimana ini bisa dilakukan?

karlstackoverflow
sumber

Jawaban:

343

Gunakan Console.KeyAvailablesehingga Anda hanya menelepon ReadKeysaat Anda tahu itu tidak akan memblokir:

Console.WriteLine("Press ESC to stop");
do {
    while (! Console.KeyAvailable) {
        // Do something
   }       
} while (Console.ReadKey(true).Key != ConsoleKey.Escape);
Jeff Sternal
sumber
6
tetapi jika Anda melakukan ini alih-alih readLine () Anda kehilangan fitur luar biasa dari memiliki riwayat penarikan dengan menekan tombol "naik".
v.oddou
3
@ v.oddou Setelah baris terakhir, cukup tambahkan Anda Console.ReadLine()dan Anda sudah siap, bukan?
Gaffi
1
Tidakkah loop ini mengkonsumsi banyak CPU / RAM? Jika tidak, bagaimana bisa begitu?
Sofia
78

Anda dapat sedikit mengubah pendekatan Anda - gunakan Console.ReadKey()untuk menghentikan aplikasi Anda, tetapi lakukan pekerjaan Anda di utas latar:

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

Dalam myWorker.DoStuff()fungsi ini Anda akan memanggil fungsi lain pada utas latar belakang (menggunakan Action<>()atau Func<>()merupakan cara mudah untuk melakukannya), lalu segera kembali.

pemalas
sumber
60

Cara terpendek:

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

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

Console.ReadKey()adalah fungsi pemblokiran, menghentikan eksekusi program dan menunggu penekanan tombol, tetapi berkat pemeriksaan Console.KeyAvailablepertama, whileloop tidak diblokir, tetapi berjalan sampai Escditekan.

Yuliia Ashomok
sumber
Contoh termudah sejauh ini. Terima kasih.
joelc
18

Dari Bangunan kutukan video. Aplikasi Konsol NET di C # oleh Jason Roberts di http://www.pluralsight.com

Kita bisa melakukan beberapa proses berikut untuk menjalankan

  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
sumber
2
Bisakah Anda jelaskan sedikit tentang pendekatan yang Anda punya saya mencoba melakukan hal yang sama dengan aplikasi konsol saya. Penjelasan untuk kode akan menjadi besar.
nayef harb
@nayefharb mengatur banyak tugas, memulai, dan WaitAll akan menunggu sampai semuanya kembali. Karenanya tugas individu tidak akan kembali dan akan berlanjut selamanya sampai CancelKeyPress dipicu.
wonea
Console.CursorVisible = false;akan lebih baik untuk menghindari gangguan kursor berkedip. Tambahkan baris ini sebagai baris pertama di static void Main(string[] args)blok.
Hitesh
12

Berikut adalah pendekatan bagi Anda untuk melakukan sesuatu pada utas yang berbeda dan mulai mendengarkan tombol yang ditekan pada utas yang berbeda. Dan Konsol akan berhenti memprosesnya saat proses Anda yang sebenarnya berakhir atau pengguna menghentikan proses dengan menekan Esctombol.

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
sumber
5

Jika Anda menggunakan Visual Studio, maka Anda dapat menggunakan "Mulai Tanpa Debugging" di menu Debug.

Secara otomatis akan menulis "Tekan tombol apa saja untuk melanjutkan ..." ke konsol untuk Anda setelah menyelesaikan aplikasi dan itu akan meninggalkan konsol terbuka untuk Anda sampai tombol ditekan.

LVBen
sumber
3

Mengatasi kasus-kasus yang beberapa jawaban lainnya tidak ditangani dengan baik:

  • Responsif : eksekusi langsung kode penanganan penekanan tombol; menghindari keanehan polling atau memblokir penundaan
  • Opsionalitas : penekanan tombol global adalah opt-in ; jika tidak, aplikasi akan keluar secara normal
  • Pemisahan masalah : kode pendengaran yang kurang invasif; beroperasi secara independen dari kode aplikasi konsol normal.

Banyak solusi di halaman ini melibatkan polling Console.KeyAvailableatau pemblokiran Console.ReadKey. Meskipun memang benar. NET Consoletidak terlalu kooperatif di sini, Anda dapat menggunakan Task.Rununtuk beralih ke Asyncmode mendengarkan yang lebih modern .

Masalah utama yang harus diperhatikan adalah bahwa, secara default, utas konsol Anda tidak diatur untuk Asyncoperasi - artinya, ketika Anda keluar dari bagian bawah mainfungsi Anda , alih-alih menunggu Asyncpenyelesaian, AppDoman Anda dan prosesnya akan berakhir . Cara yang tepat untuk mengatasinya adalah dengan menggunakan AsyncContext karya Stephen Cleary untuk membangun Asyncdukungan penuh dalam program konsol single-threaded Anda. Tetapi untuk kasus yang lebih sederhana, seperti menunggu penekanan tombol, memasang trampolin penuh mungkin berlebihan.

Contoh di bawah ini adalah untuk program konsol yang digunakan dalam beberapa jenis file batch berulang. Dalam hal ini, ketika program selesai dengan pekerjaannya, biasanya ia harus keluar tanpa perlu menekan tombol, dan kemudian kami mengizinkan penekanan tombol opsional untuk mencegah aplikasi keluar. Kita dapat menjeda siklus untuk memeriksa hal-hal, mungkin melanjutkan, atau menggunakan jeda sebagai 'titik kontrol' yang dikenal di mana untuk membersihkan file batch.

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
sumber
1

dengan kode di bawah ini Anda dapat mendengarkan penekanan SpaceBar, di tengah-tengah eksekusi konsol Anda dan jeda sampai tombol lain ditekan dan juga mendengarkan EscapeKey untuk melanggar loop utama

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
sumber
-1

Menurut pengalaman saya, di aplikasi konsol cara termudah untuk membaca tombol terakhir yang ditekan adalah sebagai berikut (Contoh dengan tombol panah):

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

Saya menggunakan untuk menghindari loop, alih-alih saya menulis kode di atas dalam suatu metode, dan saya menyebutnya di akhir "Method1" dan "Method2", jadi, setelah menjalankan "Method1" atau "Method2", Console.ReadKey () .Key siap membaca kunci lagi.

Dhemmlerf
sumber