Detecting SINGLE key presses in CONSOLE C#

元气小坏坏 提交于 2019-12-22 08:36:16

问题


I'm new to coding and have decided to start with C#. I've decided to write a simple console program that will detect key press and if only Enter is pressed, it will show number. The problem is that you can just hold down key and it will continue to show numbers. What should I add to my code so program will detect only SINGLE presses and ignore if 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 in Web at all)

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

回答1:


If your task is to count amount of changes of typed/holded keys you can do it with just remembering of 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 single key you can use StopWatch class to check time lapsed from previous key had come (in the example 300 is only for testing, I would suggest to research it deeply if that is fit your goal)

var sw = Stopwatch.StartNew();
do
{
    var x = Console.ReadKey();
    if (sw.ElapsedMilliseconds > 300)
    {
        Console.WriteLine(++index);
    }
    sw.Restart();
} while (index < 10);

or combine both of the ways




回答2:


Yes, you cant use ReadKey here. I would recommend to use ReadConsoleInput WinApi function. You can write 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
}

Example of usage:

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



回答3:


Did you try to add a method keyRelease like that in your loop for after num++ : WaitForKeyRelease(ConsoleKey.Enter);

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

You wait until the key pressed is not enter so it's like a key release.




回答4:


You can check if "enter" is inputted by doing something like this:

`

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

`



来源:https://stackoverflow.com/questions/45393158/detecting-single-key-presses-in-console-c-sharp

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!