【问题标题】:Show keyboard inputs live as they are typed (C# Console app)在键入时实时显示键盘输入(C# 控制台应用程序)
【发布时间】:2020-01-21 10:45:41
【问题描述】:

我现在正在尝试创建一个基本程序,在输入时显示实时输入。示例输出如下所示:

当前输入:CTRL ALT S V SPACE

(请注意,这些将是当前持有的键。如果用户释放 CTRL 键,该输入将消失)。 我在这里创建了一些示例代码。这很粗糙,但这是我最好的尝试。它在一个更大的程序(我将其用作沙箱)中,但唯一需要的上下文是 using System; 位于程序的顶部

    public static void KeypressTest()
    {
        char[] keys = new char[10];
        int x;
        while (1==1)
        {
            for (x = 0; x < 10; x++) //All the loops attempt to fill the array if possible. I figured this was the easiest way to store multiple characters at once
            {
                keys[x] = Convert.ToChar(ConsoleKey.MediaStop); //I don't know how to set it to blank, so I picked a character that isn't likely to be used
            }
            for (x = 0; x < 10; x++)
            {
                if (keys[x] != Convert.ToChar(ConsoleKey.MediaStop)) { x += 1; } //This skips the key if it is not the default (MediaStop). This is temporary and will be altered once I can figure out how to register a key has been lifted
                else { keys[x] = Console.ReadKey().KeyChar; }
            }
            Console.Write("\rCurrent inputs: \n");
            for (x = 0; x < 10; x++)
            { Console.Write(Convert.ToString(keys[x])); }
            Console.WriteLine();
        }
    }

代码本身存在等待所有 10 个输入的问题,然后显示键入的键,而忽略键的释放。它似乎更像是一个键盘记录器,而不是显示当前持有的键的目标。如果用户输入“大家好!!!!!”,示例输出将如下所示:

当前输入:

你好

当前输入:

你们!!!!!!

目标会在握住它的那一刻显示每个键,一旦释放就不会显示它(但我不知道该怎么做)。

问题是,我对 C# 很陌生,还没有找到在输入时更新输入的方法(我知道 Console.ReadKey() 但我不知道如何让它运行在后台。我知道线程是什么,但不知道如何做,所以任何提示将不胜感激。谢谢!

【问题讨论】:

  • 欢迎来到 Stack Overflow!请提供一些代码显示您已经尝试过的步骤。
  • 必须是控制台程序吗?可以是windows窗体程序吗?
  • @AntiqTech 是的,它必须是控制台。我对 Windows 窗体完全没有经验(我目前是一名新的 A-Level 学生,所以我们还没有了解它们)。
  • @Adrian 谢谢!我将使用我可以挖掘的任何代码更新请求(我目前不在可以访问 IDE 的计算机上)

标签: c# console


【解决方案1】:

我已经改变了我的答案,这更接近你想要的。我在这个线程(NativeKeyboard 内部类)中找到了一个 keydown 事件实现: C# arrow key input for a console app

解决方案在于多个线程计时器一起检查值或条件。主程序线程处于循环中,直到满足退出条件(按 ESC 键)。有一个提示器计时器,它打印表示按下的键列表的值的缓存,一行一行地用空格清除。对于每个 ConsoleKey 枚举,都会创建一个计时器并映射 timer-key duo。每当按下某个键时,该键的映射计时器将能够确定按下了该特定键并使用键的值/字符串更新列表。每当释放键时,列表也会更新为该键的空字符串值。

我必须添加 LocalModifiers 枚举作为 ALT、CTRL、SHIFT 和 CAPS LOCK 的键码。

我必须使用 lock() 来确保所有这些计时器都可以工作而不会产生并发问题。

当我执行代码时,它的输出与您描述的非常接近。有时在按下 7-8 个键的情况下,一些按下的键不显示。

这是我的代码:

 class Program
{
    public static Dictionary<int, String> inputMap = new Dictionary<int, string>();
    public static Dictionary<Timer, ConsoleKey> TimerKeyMap = new Dictionary<Timer, ConsoleKey>();
    public static Dictionary<Timer, LocalModifiers> TimerModifierMap = new Dictionary<Timer, LocalModifiers>();
    public static bool continueLoop = true;
    public static object locker = new object();
    static void Main(string[] args)
    {
        Program program = new Program();
        program.run();
    }
    public enum LocalModifiers :int
    {
        SHIFT = 0x10,
        CTL = 0x11,
        ALT = 0x12,
        CAPS_LOCK = 0x14
    }
    public void run()
    {
        Timer keyPressedPrompter = new Timer();
        keyPressedPrompter.Interval = 60;
        keyPressedPrompter.Elapsed += new ElapsedEventHandler(KeyPressedPrompterEvent);
        keyPressedPrompter.Enabled = true;
        foreach (ConsoleKey key in Enum.GetValues(typeof(ConsoleKey)))
        {
            Timer timer = new Timer();
            TimerKeyMap[timer] = key;
            timer.Interval = 60;
            timer.Elapsed += new ElapsedEventHandler(KeyPressedCheckerEvent);
            timer.Enabled = true;
        }
        foreach (LocalModifiers key in Enum.GetValues(typeof(LocalModifiers)))
        {
            Timer timer = new Timer();
            TimerModifierMap[timer] = key;
            timer.Interval = 60;
            timer.Elapsed += new ElapsedEventHandler(ModifierPressedCheckerEvent);
            timer.Enabled = true;
        }
        Console.WriteLine("Current inputs:");
        while (continueLoop) { }
    }
    public static void ModifierPressedCheckerEvent(object source, EventArgs e)
    {
        lock (locker)
        {
            if (NativeKeyboard.IsKeyDown((int)TimerModifierMap[(Timer)source]))
            {
                //Console.WriteLine(KeyTimerMapReverse[(Timer)source].ToString()+ " pressed");
                inputMap[(int)TimerModifierMap[(Timer)source]] = TimerModifierMap[(Timer)source].ToString() + " ";
            }
            else
            {
                // Console.WriteLine(KeyTimerMapReverse[(Timer)source].ToString() + " released");
                inputMap[(int)TimerModifierMap[(Timer)source]] = "";

            }
        }
    }
    public static void KeyPressedCheckerEvent(object source, EventArgs e)
    {
        lock (locker)
        {
            if (NativeKeyboard.IsKeyDown((int)TimerKeyMap[(Timer)source]))
            {
                if (TimerKeyMap[(Timer)source] == ConsoleKey.Escape)
                    continueLoop = false;
                //Console.WriteLine(KeyTimerMapReverse[(Timer)source].ToString()+ " pressed");
                inputMap[(int)TimerKeyMap[(Timer)source]] = TimerKeyMap[(Timer)source].ToString() + " ";
            }
            else
            {
                // Console.WriteLine(KeyTimerMapReverse[(Timer)source].ToString() + " released");
                inputMap[(int)TimerKeyMap[(Timer)source]] = "";

            }
        }
    }
    public static void KeyPressedPrompterEvent(object source, EventArgs e)
    {
        Console.Write("                                             ");//clear the line - -  can be extended
        Console.Write("\r");

        lock (locker)
        {
            foreach (KeyValuePair<int, String> entry in inputMap)
            {
                // do something with entry.Value or entry.Key
                Console.Write(entry.Value);
            }
        }

    }
}

/// <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(int 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);
}

【讨论】:

  • 这很完美!我将确保我花一两个晚上来学习它是如何工作的,这样我就可以为未来自己做这件事,但仍然如此。非常感谢!
  • 如果您对解决方案有任何疑问,可以询问。我会尽量回答。
猜你喜欢
  • 1970-01-01
  • 2011-11-23
  • 2015-06-07
  • 2011-05-20
  • 2010-09-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多