.NET konsol uygulamasında tuşa basarak dinleyin


224

Bir tuşa basana kadar konsol uygulamamı çalıştırmaya nasıl devam edebilirim ( Escbasıldığı gibi ?)

Ben bir süre döngü sarılmış varsayıyorum. ReadKeyİşlemi engellediğinden ve bir tuş istediğinden hoşlanmıyorum , sadece tuşa basmayı dinlemek ve dinlemek yerine.

Bu nasıl yapılabilir?

Yanıtlar:


343

Console.KeyAvailableYalnızca ReadKeyengellemeyeceğini bildiğinizde aramak için kullanın :

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

6
ancak bunu readLine () yerine yaparsanız, "yukarı" tuşuna basarak geçmiş hatırlamanın harika özelliğini kaybedersiniz.
v.oddou

3
@ v.oddou Son satırdan sonra, sadece ekleyin Console.ReadLine()ve hazırsınız, değil mi?
Gaffi

1
Bu döngü çok fazla CPU / RAM tüketmeyecek mi? Değilse, nasıl?
Sofya

79

Yaklaşımınızı biraz değiştirebilirsiniz - Console.ReadKey()uygulamanızı durdurmak için kullanın , ancak çalışmanızı bir arka plan iş parçacığında yapın:

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

İşlevde, myWorker.DoStuff()daha sonra arka plan iş parçacığında başka bir işlevi çağırırsınız ( Action<>()ya Func<>()da bunu yapmak için kolay bir yoldur), sonra hemen geri dönersiniz.


60

En kısa yol:

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

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

Console.ReadKey()bir bloke edici özelliği, bu program ve bir tuşa basılması için bekler yürütülmesini durdurur, ancak sayesinde kontrol için Console.KeyAvailableilk whileilmek bloke edilmez, ancak kadar çalışan Escbastırılır.


Şimdiye kadarki en kolay örnek. Teşekkür ederim.
19:11, joelc

18

Http://www.pluralsight.com adresindeki Jason Roberts tarafından C # ile .NET Konsolu Uygulamaları Oluşturma video lanetinden

Birden fazla çalışma sürecine sahip olmak için aşağıdakileri yapabiliriz

  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
Konsol uygulamamla aynı şeyi yapmaya çalıştığım yaklaşım hakkında biraz açıklayabilir misiniz. Kod için bir açıklama harika olurdu.
nayef harb

@nayefharb bir grup görevi ayarlar, başlatır ve WaitAll hepsi geri dönene kadar bekler. Bu nedenle tek tek görevler geri dönmez ve bir CancelKeyPress tetiklenene kadar sonsuza kadar devam eder.
wonea

Console.CursorVisible = false;imleç yanıp sönme hatalarından kaçınmak için en iyisi olacaktır. Bu satırı static void Main(string[] args)bloktaki ilk satır olarak ekleyin .
Hitesh

12

İşte size farklı bir evrede bir şeyler yapmak ve farklı bir evrede basılan tuşu dinlemeye başlamak için bir yaklaşım. Ve Konsol, asıl işleminiz sona erdiğinde veya kullanıcı işlemi tuşuna basarak sonlandırdığında işlemini durduracaktır 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;

        }
    }

}

5

Visual Studio kullanıyorsanız, hata ayıklama menüsünde "Hata ayıklama olmadan Başlat" kullanabilirsiniz.

Otomatik olarak "Devam etmek için herhangi bir tuşa basın." Yazacaktır. Uygulamayı tamamladıktan sonra sizin için konsola yönlendirin ve bir tuşa basılana kadar konsolu sizin için açık bırakın.


3

Diğer cevapların bazılarının iyi işlemediği durumları ele almak:

  • Duyarlı : tuşa basma işlem kodunun doğrudan yürütülmesi; yoklama veya engelleme gecikmelerinin değişkenliklerini önler
  • İsteğe bağlılık: global tuşa basma seçeneği ; aksi takdirde uygulama normal şekilde çıkmalıdır
  • Endişelerin ayrılması : daha az invaziv dinleme kodu; normal konsol uygulama kodundan bağımsız olarak çalışır.

Bu sayfadaki çözümlerin çoğu yoklama Console.KeyAvailableveya engelleme içeriyor Console.ReadKey. ConsoleBurada .NET'in çok fazla işbirliği yapmadığı doğru olsa da , Task.Rundaha modern bir Asyncdinleme moduna geçmek için kullanabilirsiniz .

Dikkat edilmesi gereken temel sorun, varsayılan olarak, konsol iş parçacığınızın Asyncçalışma için ayarlanmadığıdır - yani, mainişlevlerinizi Asynctamamladığınızda, tamamlamayı beklemek yerine AppDoman ve işleminizin sona ereceği . Bunu ele almanın uygun bir yolu , tek iş parçacıklı konsol programınızda tam destek sağlamak için Stephen Cleary'nin AsyncContext'ini kullanmak olacaktır Async. Ancak bir tuşa basmayı beklemek gibi daha basit durumlar için, tam bir trambolin takmak aşırı olabilir.

Aşağıdaki örnek, bir tür yinelemeli toplu iş dosyasında kullanılan bir konsol programı için olabilir. Bu durumda, program çalışmasıyla tamamlandığında, normalde bir tuşa basmaya gerek kalmadan çıkmalıdır ve daha sonra uygulamanın çıkmasını önlemek için isteğe bağlı bir tuşa basmaya izin veriyoruz . Muhtemelen devam edecek bir şeyleri incelemek için döngüyü duraklatabilir veya duraklamayı, toplu iş dosyasından temiz bir şekilde kurtulmak için bilinen bir 'kontrol noktası' olarak kullanabiliriz.

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

Aşağıdaki kodla, konsol yürütmenizin ortasında SpaceBar tuşuna basmayı dinleyebilir ve başka bir tuşa basılana kadar duraklatabilir ve ayrıca ana döngüyü kırmak için EscapeKey'i dinleyebilirsiniz.

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

Deneyimlerime göre, konsol uygulamalarında, basılan son anahtarı okumanın en kolay yolu aşağıdaki gibidir (Ok tuşlarıyla örnek):

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

Döngüler önlemek için kullanın, bunun yerine yukarıdaki bir yöntem içinde kod yazmak ve "Method1" veya "Method2" sonunda, bu nedenle, "Method1" veya "Method2", Console.ReadKey () yürüttükten sonra çağırıyorum Tuşları tekrar okumaya hazır.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.