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

      

+3


source to share


4 answers


Have you tried adding a keyRelease method like in your loop for num ++: WaitForKeyRelease (ConsoleKey.Enter);

And in your method: while (Console.ReadKey (true) .Key == key)



You wait until the pressed key is entered so that it is pressed by the key.

0


source


You can check if "enter" has been entered by doing something like this:

`



string input = Console.ReadLine();
if (input == "") {
     //do stuff
}

      

`



0


source


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

0


source


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

      

0


source







All Articles