ฉันจะเรียกใช้แอปพลิเคชั่นคอนโซลของฉันต่อไปได้อย่างไรจนกว่าจะกดปุ่ม (เช่นEscกด)
ฉันสมมติว่ามันวนรอบสักครู่ ฉันไม่ชอบReadKey
ที่จะบล็อกการดำเนินการและขอคีย์แทนที่จะทำต่อและฟังการกดปุ่ม
สิ่งนี้สามารถทำได้?
ฉันจะเรียกใช้แอปพลิเคชั่นคอนโซลของฉันต่อไปได้อย่างไรจนกว่าจะกดปุ่ม (เช่นEscกด)
ฉันสมมติว่ามันวนรอบสักครู่ ฉันไม่ชอบReadKey
ที่จะบล็อกการดำเนินการและขอคีย์แทนที่จะทำต่อและฟังการกดปุ่ม
สิ่งนี้สามารถทำได้?
คำตอบ:
ใช้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.ReadKey()
เพื่อหยุดแอพของคุณ แต่ทำงานในหัวข้อพื้นหลัง:
static void Main(string[] args)
{
var myWorker = new MyWorker();
myWorker.DoStuff();
Console.WriteLine("Press any key to stop...");
Console.ReadKey();
}
ในmyWorker.DoStuff()
ฟังก์ชั่นคุณจะต้องเรียกใช้ฟังก์ชันอื่นบนเธรดพื้นหลัง (การใช้Action<>()
หรือFunc<>()
เป็นวิธีที่ทำได้ง่าย) จากนั้นส่งคืนทันที
วิธีที่สั้นที่สุด:
Console.WriteLine("Press ESC to stop");
while (!(Console.KeyAvailable && Console.ReadKey(true).Key == ConsoleKey.Escape))
{
// do something
}
Console.ReadKey()
เป็นฟังก์ชั่นการบล็อคมันจะหยุดการทำงานของโปรแกรมและรอการกดปุ่ม แต่ต้องขอบคุณการตรวจสอบConsole.KeyAvailable
ครั้งแรกwhile
ลูปจะไม่ถูกบล็อก แต่จะทำงานจนกว่าEscจะกด
จากวิดีโอแช่งการสร้างแอพพลิเคชันคอนโซล NET ใน C # โดย Jason Roberts ที่http://www.pluralsight.com
เราสามารถทำตามเพื่อให้มีกระบวนการทำงานหลายขั้นตอน
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;
}
}
}
หากคุณใช้ Visual Studio คุณสามารถใช้ "เริ่มต้นโดยไม่มีการดีบัก" ในเมนูดีบั๊ก
มันจะเขียน "กดปุ่มใดก็ได้เพื่อดำเนินการต่อ.." ไปยังคอนโซลสำหรับคุณเมื่อเสร็จสิ้นแอปพลิเคชันและจะเปิดคอนโซลให้คุณจนกว่าจะมีการกดปุ่ม
การพูดถึงกรณีที่คำตอบอื่น ๆ บางคำตอบไม่ได้ดี:
หลายของการแก้ปัญหาในหน้านี้ที่เกี่ยวข้องกับการเลือกตั้งหรือการปิดกั้นConsole.KeyAvailable
Console.ReadKey
ในขณะที่มันเป็นความจริงที่. NET Console
ไม่ร่วมมือกันมากที่นี่คุณสามารถใช้Task.Run
เพื่อไปสู่Async
โหมดการฟังที่ทันสมัยยิ่งขึ้น
ปัญหาหลักที่ต้องระวังคือโดยปกติแล้วเธรดคอนโซลของคุณจะไม่ได้รับการตั้งค่าสำหรับAsync
การดำเนินการ - ซึ่งหมายความว่าเมื่อคุณหลุดออกมาจากด้านล่างของmain
ฟังก์ชั่นแทนที่จะรอความAsync
สำเร็จ AppDoman ของคุณและกระบวนการจะสิ้นสุดลง . วิธีที่เหมาะสมในการจัดการเรื่องนี้คือการใช้AsyncContextของ Stephen Cleary เพื่อสร้างAsync
การสนับสนุนอย่างเต็มที่ในโปรแกรมคอนโซลแบบเธรดเดี่ยวของคุณ แต่สำหรับกรณีที่ง่ายกว่าเช่นรอปุ่มกดการติดตั้งแทรมโพลีนแบบเต็มอาจเกินความจำเป็น
ตัวอย่างด้านล่างนี้สำหรับโปรแกรมคอนโซลที่ใช้ในไฟล์ชุดซ้ำบางประเภท ในกรณีนี้เมื่อโปรแกรมทำงานกับโปรแกรมปกติแล้วควรออกโดยไม่ต้องใช้ปุ่มกดจากนั้นเราอนุญาตให้กดปุ่มเสริมเพื่อป้องกันไม่ให้แอปออกจากโปรแกรม เราสามารถหยุดวงจรชั่วคราวเพื่อตรวจสอบสิ่งต่าง ๆ อาจกลับมาทำงานต่อหรือใช้การหยุดชั่วคราวเป็น 'จุดควบคุม' ที่เป็นที่รู้จักซึ่งจะแบ่งไฟล์แบตช์ออกอย่างหมดจด
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
}
ฉันใช้เพื่อหลีกเลี่ยงการวนซ้ำแทนฉันเขียนรหัสข้างต้นภายในวิธีการและฉันเรียกมันในตอนท้ายของทั้ง "วิธีที่ 1" และ "วิธีที่ 2" ดังนั้นหลังจากดำเนินการ "Method1" หรือ "Method2", Console.ReadKey () .Key พร้อมที่จะอ่านกุญแจอีกครั้ง