控制台应用程序的 C# 箭头键输入
Posted
技术标签:
【中文标题】控制台应用程序的 C# 箭头键输入【英文标题】:C# arrow key input for a console app 【发布时间】:2011-05-20 01:43:44 【问题描述】:我有一个用 C# 编写的简单控制台应用程序。我希望能够检测到方向键按下,这样我就可以让用户转向。如何使用控制台应用检测 keydown/keyup 事件?
我所有的谷歌搜索都导致了有关 Windows 窗体的信息。我没有图形用户界面。这是一个控制台应用程序(通过串行端口控制机器人)。
我编写了处理这些事件的函数,但我不知道如何注册才能实际接收这些事件:
private void myKeyDown(object sender, KeyEventArgs e)
switch (e.KeyCode)
case Keys.Left:
...
case Keys.Right:
...
case Keys.Up:
...
private void myKeyUp(object sender, KeyEventArgs e)
... pretty much the same as myKeyDown
这可能是一个非常基本的问题,但我对 C# 还很陌生,而且我以前从未需要过这种输入。
更新:很多人建议我使用System.Console.ReadKey(true).Key
。这无济于事。我需要知道一个键被按下的那一刻,当它被释放时,支持同时按住多个键。此外,ReadKey 是一个阻塞调用——这意味着程序将停止并等待按键被按下。
更新:似乎唯一可行的方法是使用 Windows 窗体。这很烦人,因为我不能在无头系统上使用它。要求表单 GUI 接收键盘输入是……愚蠢的。
但无论如何,为了后代,这是我的解决方案。我在我的 .sln 中创建了一个新的 Form 项目:
private void Form1_Load(object sender, EventArgs e)
try
this.KeyDown += new KeyEventHandler(Form1_KeyDown);
this.KeyUp += new KeyEventHandler(Form1_KeyUp);
catch (Exception exc)
...
void Form1_KeyDown(object sender, KeyEventArgs e)
switch (e.KeyCode)
// handle up/down/left/right
case Keys.Up:
case Keys.Left:
case Keys.Right:
case Keys.Down:
default: return; // ignore other keys
private void Form1_KeyUp(object sender, KeyEventArgs e)
// undo what was done by KeyDown
请注意,如果您按住某个键,KeyDown 将被多次调用,而 KeyUp 只会被调用一次(当您释放它时)。所以你需要优雅地处理重复的 KeyDown 调用。
【问题讨论】:
您可以在不同的线程中调用 ReadKey,请参阅我的旋转线程建议。虽然这并没有解决 keyup/keydown 的问题。 【参考方案1】:现在有点晚了,但这里是在控制台应用程序中访问键盘状态的方法。
请注意,它并不都是托管代码,因为它需要从 User32.dll 导入 GetKeyState。
/// <summary>
/// Codes representing keyboard keys.
/// </summary>
/// <remarks>
/// Key code documentation:
/// http://msdn.microsoft.com/en-us/library/dd375731%28v=VS.85%29.aspx
/// </remarks>
internal enum KeyCode : int
/// <summary>
/// The left arrow key.
/// </summary>
Left = 0x25,
/// <summary>
/// The up arrow key.
/// </summary>
Up,
/// <summary>
/// The right arrow key.
/// </summary>
Right,
/// <summary>
/// The down arrow key.
/// </summary>
Down
/// <summary>
/// Provides keyboard access.
/// </summary>
internal static class NativeKeyboard
/// <summary>
/// A positional bit flag indicating the part of a key state denoting
/// key pressed.
/// </summary>
private const int KeyPressed = 0x8000;
/// <summary>
/// Returns a value indicating if a given key is pressed.
/// </summary>
/// <param name="key">The key to check.</param>
/// <returns>
/// <c>true</c> if the key is pressed, otherwise <c>false</c>.
/// </returns>
public static bool IsKeyDown(KeyCode key)
return (GetKeyState((int)key) & KeyPressed) != 0;
/// <summary>
/// Gets the key state of a key.
/// </summary>
/// <param name="key">Virtuak-key code for key.</param>
/// <returns>The state of the key.</returns>
[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern short GetKeyState(int key);
【讨论】:
有趣。这需要我自己轮询键盘,这意味着我不能只拥有一个基于事件的应用程序。但这很简单。 是的,控制台应用程序通常没有消息循环,但没有什么可以阻止您自己滚动,然后让应用程序的其余部分成为事件驱动的。【参考方案2】:var isUp = Console.ReadKey().Key == ConsoleKey.UpArrow;
或另一个示例,仅针对您的情况:
while (true)
var ch = Console.ReadKey(false).Key;
switch(ch)
case ConsoleKey.Escape:
ShutdownRobot();
return;
case ConsoleKey.UpArrow:
MoveRobotUp();
break;
case ConsoleKey.DownArrow:
MoveRobotDown();
break;
【讨论】:
【参考方案3】:System.Console.ReadKey(true).Key == ConsoleKey.UpArrow
你可以把它变成一个旋转,比如:
while(Running)
DoStuff();
System.Console.ReadKey(true).Key == ConsoleKey.UpArrow
Thread.Sleep(1)
【讨论】:
按住一个键,还是按住多个键呢?我不只是想按下最新的键 - 我想处理多个键被按住(或根本没有键按下)。 那你需要直接处理键盘消息。最简单的方法是在 Windows 上使用 DirectInput。 如果一定要手动的话,相关的Win32函数有GetConsoleWindow和SetWindowsHookEx。挂钩控制台的 'WH_CALLWNDPROC' 事件以捕获原始 Windows 消息。控制台并没有获得 Windows 窗体所做的太多抽象,尽管它可能具有您正在寻找的功能,事实上没有人建议它向我暗示这是您的下一个合乎逻辑的步骤。 如果您打算创建一个高度交互的应用程序,您最好在 Windows 窗体中创建一个人造控制台。这是一种用于创建基本视频游戏 UI 的流行技术。 请记住,在键盘上的每个键与 shift、ctrl、alt 等修饰符之间,您可以使用数百个组合键。因此,为什么想要更多功能并不完全是标准功能。【参考方案4】:我和你发现了同样的问题,在这里,一个使用任务的有趣帖子。 原始帖子可以在这里找到: C# Console Application - How do I always read input from the console?
我必须通过 Raspberry GPIO(使用单声道 C#)模拟 PWM 输出来测试 LCD 背光。我想用两个简单的键来改变占空比(上/下)和一个额外的键来停止程序。
我试过这个(变量):
static ConsoleKeyInfo key = new ConsoleKeyInfo();
static int counter = 0;
static int duty = 5; // Starts in 50%
主程序:
static void Main(string[] args)
// cancellation by keyboard string
CancellationTokenSource cts = new CancellationTokenSource();
// thread that listens for keyboard input
var kbTask = Task.Run(() =>
while (true)
key = Console.ReadKey(true);
if (key.KeyChar == 'x' || key.KeyChar == 'X')
cts.Cancel();
break;
else if (key.KeyChar == 'W' || key.KeyChar == 'w')
if (duty < 10)
duty++;
//Console.WriteLine("\tIncrementa Ciclo");
//mainAppState = StateMachineMainApp.State.TIMER;
//break;
else if (key.KeyChar == 'S' || key.KeyChar == 's')
if (duty > 0)
duty--;
//Console.WriteLine("\tDecrementa Ciclo");
//mainAppState = StateMachineMainApp.State.TIMER;
// break;
);
// thread that performs main work
Task.Run(() => DoWork(), cts.Token);
string OsVersion = Environment.OSVersion.ToString();
Console.WriteLine("Sistema operativo: 0", OsVersion);
Console.WriteLine("Menú de Progama:");
Console.WriteLine(" W. Aumentar ciclo útil");
Console.WriteLine(" S. Disminuir ciclo útil");
Console.WriteLine(" X. Salir del programa");
Console.WriteLine();
// keep Console running until cancellation token is invoked
kbTask.Wait();
static void DoWork()
while (true)
Thread.Sleep(50);
if (counter < 10)
if (counter < duty)
Console.Write("─");
//Console.WriteLine(counter + " - ON");
else
Console.Write("_");
//Console.WriteLine(counter + " - OFF");
counter++;
else
counter = 0;
当需要增加占空比时,按'W'键使主任务改变占空比变量(duty);与'S'键减量相同。当按下“X”键时,程序结束。
【讨论】:
【参考方案5】:我晚了 10 年,但我想与其他可能在未来阅读此主题的人分享。
有一种更简单的方法可以实现 Ergwun 所说的。
您可以在一行代码中实现结果,而不是创建 Enum 和 Class。
[System.Runtime.InteropServices.DllImport("user32.dll")]
static extern short GetAsyncKeyState(int key);
. . .
bool[] keys = new bool[4];
for (int i = 0; i < 4; i++)
keys[i] = (0x8000 & GetAsyncKeyState((char)"\x25\x26\x27\x28"[i])) != 0;
您需要从 User32.dll 导入 GetKeyState
或在我的情况下为 GetAsyncKeyState
方法。
然后你只需要循环并检查是否有任何键被按下。 字符串中这些奇怪的数字分别是左、上、右和下箭头键的Virtual-Key Codes。
布尔结果存储在一个数组中,稍后可以循环查看是否按下了某个键。您只需要为您的程序定义数组中键的顺序
【讨论】:
【参考方案6】:你可以这样做
bool keyWasPressed = false;
if (consolekey.avalable)
keyvar = console.readkey(true);
keyWasPressed = true;
if(keyWasPressed)
// enter you code here using keyvar
else
// the commands that happen if you don't press anything
【讨论】:
以上是关于控制台应用程序的 C# 箭头键输入的主要内容,如果未能解决你的问题,请参考以下文章