Nghe phím bấm trong ứng dụng bảng điều khiển .NET


224

Làm cách nào tôi có thể tiếp tục chạy ứng dụng bảng điều khiển của mình cho đến khi nhấn phím (như Escđược nhấn?)

Tôi giả sử nó quấn quanh một vòng lặp. Tôi không thích ReadKeyvì nó chặn hoạt động và yêu cầu một phím, thay vì chỉ tiếp tục và lắng nghe nhấn phím.

Điều này có thể giải quyết như thế nào?

Câu trả lời:


343

Sử dụng Console.KeyAvailableđể bạn chỉ gọi ReadKeykhi bạn biết nó sẽ không chặn:

Console.WriteLine("Press ESC to stop");
do {
    while (! Console.KeyAvailable) {
        // Do something
   }       
} while (Console.ReadKey(true).Key != ConsoleKey.Escape);

6
nhưng nếu bạn làm điều này thay vì readLine (), bạn sẽ mất tính năng tuyệt vời của việc thu hồi lịch sử bằng cách nhấn phím "lên".
v.oddou

3
@ v.oddou Sau dòng cuối cùng, chỉ cần thêm bạn Console.ReadLine()và bạn đã đặt, không?
Gaffi

1
Vòng lặp này có tiêu tốn nhiều CPU / RAM không? Nếu không thì làm sao vậy?
Sofia

78

Bạn có thể thay đổi cách tiếp cận của mình một chút - sử dụng Console.ReadKey()để dừng ứng dụng của bạn, nhưng thực hiện công việc của bạn trong một chủ đề nền:

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

Trong myWorker.DoStuff()hàm sau đó bạn sẽ gọi một hàm khác trên một luồng nền (sử dụng Action<>()hoặc Func<>()là một cách dễ dàng để thực hiện), sau đó trả về ngay lập tức.


60

Con đường ngắn nhất:

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

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

Console.ReadKey()là một chức năng chặn, nó dừng thực thi chương trình và chờ nhấn phím, nhưng nhờ kiểm tra Console.KeyAvailabletrước, whilevòng lặp không bị chặn mà chạy cho đến khi Escnhấn.


Ví dụ dễ nhất cho đến nay. Cảm ơn bạn.
joelc

18

Từ lời nguyền video Xây dựng các ứng dụng Bảng điều khiển .NET trong C # của Jason Roberts tại http://www.pluralsight.com

Chúng ta có thể làm theo để có nhiều quá trình chạy

  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);
        }
    }

2
Bạn có thể giải thích một chút về cách tiếp cận mà bạn đang cố gắng làm tương tự với ứng dụng bảng điều khiển của tôi không. Một lời giải thích cho mã sẽ rất tuyệt.
nayef harb

@nayefharb thiết lập một loạt các nhiệm vụ, bắt đầu chúng và WaitAll sẽ đợi cho đến khi tất cả chúng quay trở lại. Do đó, các tác vụ riêng lẻ sẽ không quay trở lại và nó sẽ tiếp tục mãi cho đến khi CancKeyPress được kích hoạt.
wonea

Console.CursorVisible = false;sẽ là tốt nhất để tránh con trỏ nhấp nháy. Thêm dòng này là dòng đầu tiên trong static void Main(string[] args)khối.
Hitesh

12

Đây là một cách tiếp cận để bạn làm một cái gì đó trên một luồng khác và bắt đầu nghe phím được nhấn trong một luồng khác. Và Bảng điều khiển sẽ dừng quá trình xử lý khi quá trình thực tế của bạn kết thúc hoặc người dùng kết thúc quá trình bằng cách nhấn Escphím.

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;

        }
    }

}

5

Nếu bạn đang sử dụng Visual Studio, thì bạn có thể sử dụng "Bắt đầu mà không cần gỡ lỗi" trong menu Gỡ lỗi.

Nó sẽ tự động viết "Nhấn phím bất kỳ để tiếp tục .." đến bàn điều khiển cho bạn sau khi hoàn thành ứng dụng và nó sẽ để bàn điều khiển mở cho bạn cho đến khi nhấn phím.


3

Giải quyết các trường hợp mà một số câu trả lời khác không xử lý tốt:

  • Responsive : thực thi trực tiếp mã xử lý nhấn phím; tránh sự mơ hồ của việc bỏ phiếu hoặc chặn sự chậm trễ
  • Tùy chọn : nhấn phím toàn cầu là chọn tham gia ; nếu không thì ứng dụng sẽ thoát bình thường
  • Tách các mối quan tâm : mã nghe ít xâm lấn; hoạt động độc lập với mã ứng dụng giao diện điều khiển bình thường.

Nhiều giải pháp trên trang này liên quan đến việc bỏ phiếu Console.KeyAvailablehoặc chặn Console.ReadKey. Mặc dù đúng là .NET Consolekhông hợp tác ở đây, nhưng bạn có thể sử dụng Task.Runđể chuyển sang Asyncchế độ nghe hiện đại hơn .

Vấn đề chính cần lưu ý là, theo mặc định, luồng bàn điều khiển của bạn không được thiết lập để Asynchoạt động - có nghĩa là, khi bạn rơi khỏi đáy mainchức năng, thay vì chờ Asynchoàn thành, quá trình và AppDoman của bạn sẽ kết thúc . Một cách thích hợp để giải quyết vấn đề này là sử dụng AsyncContext của Stephen Clear để thiết lập Asynchỗ trợ đầy đủ trong chương trình bảng điều khiển đơn luồng của bạn. Nhưng đối với các trường hợp đơn giản hơn, như chờ nhấn phím, cài đặt một tấm bạt lò xo đầy đủ có thể là quá mức cần thiết.

Ví dụ dưới đây sẽ dành cho một chương trình giao diện điều khiển được sử dụng trong một số loại tệp lặp. Trong trường hợp này, khi chương trình được thực hiện với công việc của nó, thông thường nó sẽ thoát mà không cần nhấn phím, và sau đó chúng tôi cho phép nhấn phím tùy chọn để ngăn ứng dụng thoát. Chúng ta có thể tạm dừng chu trình để kiểm tra mọi thứ, có thể tiếp tục lại hoặc sử dụng tạm dừng như một 'điểm kiểm soát' đã biết để thoát ra khỏi tệp bó.

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
    }
}

1

với mã bên dưới, bạn có thể nghe SpaceBar nhấn, ở giữa thực thi bảng điều khiển và tạm dừng cho đến khi nhấn phím khác và cũng nghe EscapeKey để phá vỡ vòng lặp chính

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;
    }

-1

Theo kinh nghiệm của tôi, trong các ứng dụng bảng điều khiển, cách dễ nhất để đọc phím cuối cùng được nhấn là như sau (Ví dụ với các phím mũi tên):

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

Tôi sử dụng để tránh các vòng lặp, thay vào đó tôi viết mã ở trên trong một phương thức và tôi gọi nó ở cuối cả "Phương thức 1" và "Phương thức 2", vì vậy, sau khi thực hiện "Phương thức 1" hoặc "Phương thức 2", Console.ReadKey () .Key đã sẵn sàng để đọc lại các phím.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.