SINGLE keypress detection in CONSOLE C #
I am new to coding and decided to start with C #. I decided to write a simple console program that detects a key press and if only the Enter key is pressed, it will show the number. The problem is you can just hold the key and it will keep showing numbers. What should I add to my code so that the program will only detect SINGLE presses and ignore if the user is HOLDING the key? (I didn't find anything about this issue for CONSOLE C#
, only for Forms one. Neither on this forum nor on the Web at all)
Thank you in advance
static void Main(string[] args)
{
Console.WriteLine("Press Enter to play!");
int num = 0;
void WaitForKey(ConsoleKey key)
{
while (Console.ReadKey(true).Key != key)
{ }
}
for (int i = 0; i < 10; i++)
{
WaitForKey(ConsoleKey.Enter);
Console.Write("{0} ", num);
num++;
}
}
source to share
If your task is to count the number of keyed / held key changes, you can do this simply by remembering the last key
var lastChar = char.MaxValue;
var index = 0;
do
{
var x = Console.ReadKey();
if (lastChar != x.KeyChar)
{
lastChar = x.KeyChar;
Console.WriteLine(++index);
}
} while (index < 10);
If you need to define a single key, you can use the class StopWatch
to check the time elapsed since the previous key (came in example 300 for testing only, I would suggest looking deeply into it if that suits your purpose)
var sw = Stopwatch.StartNew();
do
{
var x = Console.ReadKey();
if (sw.ElapsedMilliseconds > 300)
{
Console.WriteLine(++index);
}
sw.Restart();
} while (index < 10);
or combine both paths
source to share
Yes, you cannot use it ReadKey
here. I would recommend using the ReadConsoleInput WinApi function. You can write a wrapper class for this purpose, for example:
internal class KeyboardInput
{
private readonly short _exitKey;
private readonly uint[] _keyStates = new uint[short.MaxValue];
public KeyboardInput(ConsoleKey exitKey)
{
_exitKey = (short) exitKey;
// subscribe with empty delegates to prevent null reference check before call
OnKeyDown += delegate { };
OnKeyUp += delegate { };
}
public event Action<char, short> OnKeyDown;
public event Action<char, short> OnKeyUp;
public void Run()
{
var exitKeyPressed = false;
var nRead = 0;
var records = new INPUT_RECORD[10];
var handle = GetStdHandle(STD_INPUT_HANDLE);
while (!exitKeyPressed)
{
ReadConsoleInputW(handle, records, records.Length, ref nRead);
for (var i = 0; i < nRead; i++)
{
// process only Key events
if (records[i].EventType != KEY_EVENT) continue;
// process key state
ProcessKey(records[i].KeyEvent.wVirtualKeyCode, records[i].KeyEvent.bKeyDown,
records[i].KeyEvent.UnicodeChar);
// check for exit key press
if ((exitKeyPressed = records[i].KeyEvent.wVirtualKeyCode == _exitKey) == true) break;
}
}
}
private void ProcessKey(short virtualKeyCode, uint keyState, char key)
{
if (_keyStates[virtualKeyCode] != keyState)
if (keyState == 1) OnKeyDown(key, virtualKeyCode);
else OnKeyUp(key, virtualKeyCode);
_keyStates[virtualKeyCode] = keyState;
}
#region Native methods
private const short KEY_EVENT = 0x0001;
private const int STD_INPUT_HANDLE = -10;
[DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern IntPtr GetStdHandle(int nStdHandle);
[DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern bool ReadConsoleInputW(IntPtr hConsoleInput, [Out] INPUT_RECORD[] lpBuffer, int nLength,
ref int lpNumberOfEventsRead);
[StructLayout(LayoutKind.Explicit)]
private struct INPUT_RECORD
{
[FieldOffset(0)] public readonly short EventType;
//union {
[FieldOffset(4)] public KEY_EVENT_RECORD KeyEvent;
//[FieldOffset(4)]
//public MOUSE_EVENT_RECORD MouseEvent;
//[FieldOffset(4)]
//public WINDOW_BUFFER_SIZE_RECORD WindowBufferSizeEvent;
//[FieldOffset(4)]
//public MENU_EVENT_RECORD MenuEvent;
//[FieldOffset(4)]
//public FOCUS_EVENT_RECORD FocusEvent;
}
[StructLayout(LayoutKind.Sequential)]
private struct KEY_EVENT_RECORD
{
public readonly uint bKeyDown;
public readonly short wRepeatCount;
public readonly short wVirtualKeyCode;
public readonly short wVirtualScanCode;
public readonly char UnicodeChar;
public readonly int dwControlKeyState;
}
#endregion
}
Usage example:
internal class Program
{
private static void Main(string[] args)
{
var kbInput = new KeyboardInput(ConsoleKey.Escape);
kbInput.OnKeyDown += OnKeyDown;
kbInput.OnKeyUp += OnKeyUp;
kbInput.Run();
}
private static void OnKeyDown(char key, short code)
{
Console.WriteLine($"Key pressed: {key} (virtual code: 0x{code:X})");
}
private static void OnKeyUp(char key, short code)
{
Console.WriteLine($"Key released: {key} (virtual code: 0x{code:X})");
}
}
source to share