在阅读控制台行时在 ESC 上收听

Listen on ESC while reading Console line

我想将用户输入读取到字符串中,同时仍然随时按 ESC 键做出反应,但不定义系统范围的热键。

所以当用户键入 e. G。 "Test Name" 但不是用 ENTER 键确认,而是按 ESC 键,他应该被带回主菜单。

Console.Write("Enter name: ")
if (Console.ReadLine().Contains(ConsoleKey.Escape.ToString()))
{
    goto MainMenu;
}
return Console.ReadLine();

这是我能想到的最简单的方法,但是由于 Console.ReadLine() 看不到 ESC,所以它不起作用。

在 开始输入文本 here 之前,发现按下 时对 ESC 作出反应的相当复杂的方式,但我希望它在 any时间.

这是最简单的方法:

Console.WriteLine("Press ESC to go to Main Menu");

while (!(Console.KeyAvailable && Console.ReadKey(true).Key == ConsoleKey.Escape))
{
    // your code here
}
goto MainMenu;

您可能不得不放弃使用 ReadLine 并使用 ReadKey:

自己滚动
static void Main(string[] args)
{
    Console.Clear();
    Console.Write("Enter your name and press ENTER.  (ESC to cancel): ");
    string name = readLineWithCancel();

    Console.WriteLine("\r\n{0}", name == null ? "Cancelled" : name);

    Console.ReadLine();
}

//Returns null if ESC key pressed during input.
private static string readLineWithCancel()
{
    string result = null;

    StringBuilder buffer = new StringBuilder();

    //The key is read passing true for the intercept argument to prevent
    //any characters from displaying when the Escape key is pressed.
    ConsoleKeyInfo info = Console.ReadKey(true);
    while (info.Key != ConsoleKey.Enter && info.Key != ConsoleKey.Escape)
    {
        Console.Write(info.KeyChar);
        buffer.Append(info.KeyChar);
        info = Console.ReadKey(true);
    } 

    if (info.Key == ConsoleKey.Enter)
    {
        result = buffer.ToString();
    }

    return result;
}

此代码不完整,可能需要努力使其健壮,但它应该会给您一些想法。

以防有人遇到同样的问题。我自己找到了解决方案。可以短一点,但至少它有效:

                string name;
                char CurrentChar = ' ';                    
                do
                {
                    CurrentChar = Console.ReadKey().KeyChar;
                    if (CurrentChar != '\b')
                    {
                        name = name + CurrentChar;
                        if (CurrentChar == (char)13)
                        {
                            return name;
                        }
                    }
                    else if (Console.CursorLeft >= 14)
                    {
                        name = name.Remove(name.Length - 1);
                        Console.Write(" \b");
                    }
                    else
                    {
                        Console.CursorLeft = 14;
                    }

                } while (CurrentChar != (char)27);

Chris Dunaway 的改进版 :

    public static bool CancelableReadLine(out string value)
    {
        value = string.Empty;
        var buffer = new StringBuilder();
        var key = Console.ReadKey(true);
        while (key.Key != ConsoleKey.Enter && key.Key != ConsoleKey.Escape)
        {
            if (key.Key == ConsoleKey.Backspace && Console.CursorLeft > 0)
            {
                var cli = --Console.CursorLeft;
                buffer.Remove(cli, 1);
                Console.CursorLeft = 0;
                Console.Write(new String(Enumerable.Range(0, buffer.Length + 1).Select(o => ' ').ToArray()));
                Console.CursorLeft = 0;
                Console.Write(buffer.ToString());
                Console.CursorLeft = cli;
                key = Console.ReadKey(true);
            }
            else if (Char.IsLetterOrDigit(key.KeyChar) || Char.IsWhiteSpace(key.KeyChar))
            {
                var cli = Console.CursorLeft;
                buffer.Insert(cli, key.KeyChar);
                Console.CursorLeft = 0;
                Console.Write(buffer.ToString());
                Console.CursorLeft = cli + 1;
                key = Console.ReadKey(true);
            }
            else if (key.Key == ConsoleKey.LeftArrow && Console.CursorLeft > 0)
            {
                Console.CursorLeft--;
                key = Console.ReadKey(true);
            }
            else if (key.Key == ConsoleKey.RightArrow && Console.CursorLeft < buffer.Length)
            {
                Console.CursorLeft++;
                key = Console.ReadKey(true);
            }
            else
            {
                key = Console.ReadKey(true);
            }
        }

        if (key.Key == ConsoleKey.Enter)
        {
            Console.WriteLine();
            value = buffer.ToString();
            return true;
        }
        return false;
    }
}

我没有测试太多,但至少对我有用。

可能还有很多需要改进的地方,但我想我已经用这个解决方案涵盖了大部分基础知识:

This code handles:

  • writing beyond the Console window edge
  • calling method after existing text (using Console.Write)
  • resizing Console window
  • Ctrl modifiers

This code does not handle:

  • Tab key (Normal Console.ReadLine has a bug when tabbing further than window size so chose to exclude this as it is seldom needed...)

我的代码基于 and 个答案,因此也归功于他们。

我建议尝试根据这些答案构建您自己的答案,但有时您只需要一些东西可以工作,在这种情况下,当我在我的应用程序中使用它时,这段代码对我有用:


只需创建一个新静态class,例如:

public static class XConsole
{

}

并在其中粘贴以下方法:

    public static string CancelableReadLine(out bool isCancelled)
    {
        var cancelKey = ConsoleKey.Escape;
        var builder = new StringBuilder();
        var cki = Console.ReadKey(true);
        int index = 0;
        (int left, int top) startPosition;

        while (cki.Key != ConsoleKey.Enter && cki.Key != cancelKey)
        {
            if (cki.Key == ConsoleKey.LeftArrow)
            {
                if (index < 1)
                {
                    cki = Console.ReadKey(true);
                    continue;
                }

                LeftArrow(ref index, cki);
            }
            else if (cki.Key == ConsoleKey.RightArrow)
            {
                if (index >= builder.Length)
                {
                    cki = Console.ReadKey(true);
                    continue;
                }

                RightArrow(ref index, cki, builder);
            }
            else if (cki.Key == ConsoleKey.Backspace)
            {
                if (index < 1)
                {
                    cki = Console.ReadKey(true);
                    continue;
                }

                BackSpace(ref index, cki, builder);
            }
            else if (cki.Key == ConsoleKey.Delete)
            {
                if (index >= builder.Length)
                {
                    cki = Console.ReadKey(true);
                    continue;
                }

                Delete(ref index, cki, builder);
            }
            else if (cki.Key == ConsoleKey.Tab)
            {
                cki = Console.ReadKey(true);
                continue;
            }
            else
            {
                if (cki.KeyChar == '[=11=]')
                {
                    cki = Console.ReadKey(true);
                    continue;
                }

                Default(ref index, cki, builder);
            }

            cki = Console.ReadKey(true);
        }

        if (cki.Key == cancelKey)
        {
            startPosition = GetStartPosition(index);
            ErasePrint(builder, startPosition);

            isCancelled = true;
            return string.Empty;
        }

        isCancelled = false;

        startPosition = GetStartPosition(index);
        var endPosition = GetEndPosition(startPosition.left, builder.Length);
        var left = 0;
        var top = startPosition.top + endPosition.top + 1;

        Console.SetCursorPosition(left, top);

        var value = builder.ToString();
        return value;
    }

    private static void LeftArrow(ref int index, ConsoleKeyInfo cki)
    {
        var previousIndex = index;
        index--;

        if (cki.Modifiers == ConsoleModifiers.Control)
        {
            index = 0;

            var startPosition = GetStartPosition(previousIndex);
            Console.SetCursorPosition(startPosition.left, startPosition.top);

            return;
        }

        if (Console.CursorLeft > 0)
            Console.CursorLeft--;
        else
        {
            Console.CursorTop--;
            Console.CursorLeft = Console.BufferWidth - 1;
        }
    }

    private static void RightArrow(ref int index, ConsoleKeyInfo cki, StringBuilder builder)
    {
        var previousIndex = index;
        index++;

        if (cki.Modifiers == ConsoleModifiers.Control)
        {
            index = builder.Length;

            var startPosition = GetStartPosition(previousIndex);
            var endPosition = GetEndPosition(startPosition.left, builder.Length);
            var top = startPosition.top + endPosition.top;
            var left = endPosition.left;

            Console.SetCursorPosition(left, top);

            return;
        }

        if (Console.CursorLeft < Console.BufferWidth - 1)
            Console.CursorLeft++;
        else
        {
            Console.CursorTop++;
            Console.CursorLeft = 0;
        }
    }

    private static void BackSpace(ref int index, ConsoleKeyInfo cki, StringBuilder builder)
    {
        var previousIndex = index;
        index--;

        var startPosition = GetStartPosition(previousIndex);
        ErasePrint(builder, startPosition);

        builder.Remove(index, 1);
        Console.Write(builder.ToString());

        GoBackToCurrentPosition(index, startPosition);
    }

    private static void Delete(ref int index, ConsoleKeyInfo cki, StringBuilder builder)
    {
        var startPosition = GetStartPosition(index);
        ErasePrint(builder, startPosition);

        if (cki.Modifiers == ConsoleModifiers.Control)
        {
            builder.Remove(index, builder.Length - index);
            Console.Write(builder.ToString());

            GoBackToCurrentPosition(index, startPosition);
            return;
        }

        builder.Remove(index, 1);
        Console.Write(builder.ToString());

        GoBackToCurrentPosition(index, startPosition);
    }

    private static void Default(ref int index, ConsoleKeyInfo cki, StringBuilder builder)
    {
        var previousIndex = index;
        index++;

        builder.Insert(previousIndex, cki.KeyChar);

        var startPosition = GetStartPosition(previousIndex);
        Console.SetCursorPosition(startPosition.left, startPosition.top);
        Console.Write(builder.ToString());

        GoBackToCurrentPosition(index, startPosition);
    }

    private static (int left, int top) GetStartPosition(int previousIndex)
    {
        int top;
        int left;

        if (previousIndex <= Console.CursorLeft)
        {
            top = Console.CursorTop;
            left = Console.CursorLeft - previousIndex;
        }
        else
        {
            var decrementValue = previousIndex - Console.CursorLeft;
            var rowsFromStart = decrementValue / Console.BufferWidth;
            top = Console.CursorTop - rowsFromStart;
            left = decrementValue - rowsFromStart * Console.BufferWidth;

            if (left != 0)
            {
                top--;
                left = Console.BufferWidth - left;
            }
        }

        return (left, top);
    }

    private static void GoBackToCurrentPosition(int index, (int left, int top) startPosition)
    {
        var rowsToGo = (index + startPosition.left) / Console.BufferWidth;
        var rowIndex = index - rowsToGo * Console.BufferWidth;

        var left = startPosition.left + rowIndex;
        var top = startPosition.top + rowsToGo;

        Console.SetCursorPosition(left, top);
    }

    private static (int left, int top) GetEndPosition(int startColumn, int builderLength)
    {
        var cursorTop = (builderLength + startColumn) / Console.BufferWidth;
        var cursorLeft = startColumn + (builderLength - cursorTop * Console.BufferWidth);

        return (cursorLeft, cursorTop);
    }

    private static void ErasePrint(StringBuilder builder, (int left, int top) startPosition)
    {
        Console.SetCursorPosition(startPosition.left, startPosition.top);
        Console.Write(new string(Enumerable.Range(0, builder.Length).Select(o => ' ').ToArray()));

        Console.SetCursorPosition(startPosition.left, startPosition.top);
    }

您现在可以这样称呼它:

        Console.WriteLine("Calling at start of screen");
        string text = XConsole.CancelableReadLine(out bool isCancelled);

        if (isCancelled)
        {
            //Do what you want in here, for instance: 
            return;
        }

也可以在Console.Write:

之后调用
        Console.WriteLine("Calling after Console.Write");
        Console.Write("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.");
        string text = XConsole.CancelableReadLine(out bool isCancelled);

        if (isCancelled) 
            return;

希望对您有所帮助!

oleg wx 的改进版

现在可以在这种情况下使用了

static void Main(string[] args)
{
    Console.Write("Text: ");
    CancelableReadConsoleLine(out string value);
}

public static bool CancelableReadConsoleLine(out string line)
{
    var clOffset = Console.CursorLeft;
    line = string.Empty;
    var buffer = new StringBuilder();
    var key = Console.ReadKey(true);
    while (key.Key != ConsoleKey.Enter && key.Key != ConsoleKey.Escape)
    {
        if (key.Key == ConsoleKey.Backspace && Console.CursorLeft - clOffset > 0)
        {
            var cli = Console.CursorLeft - clOffset - 1;
            buffer.Remove(cli, 1);
            Console.CursorLeft = clOffset;
            Console.Write(new string(' ', buffer.Length + 1));
            Console.CursorLeft = clOffset;
            Console.Write(buffer.ToString());
            Console.CursorLeft = cli + clOffset;
            key = Console.ReadKey(true);
        }
        else if (key.Key == ConsoleKey.Delete && Console.CursorLeft - clOffset < buffer.Length)
        {
            var cli = Console.CursorLeft - clOffset;
            buffer.Remove(cli, 1);
            Console.CursorLeft = clOffset;
            Console.Write(new string(' ', buffer.Length + 1));
            Console.CursorLeft = clOffset;
            Console.Write(buffer.ToString());
            Console.CursorLeft = cli + clOffset;
            key = Console.ReadKey(true);
        }
        else if (char.IsLetterOrDigit(key.KeyChar) || char.IsWhiteSpace(key.KeyChar))
        {
            var cli = Console.CursorLeft - clOffset;
            buffer.Insert(cli, key.KeyChar);
            Console.CursorLeft = clOffset;
            Console.Write(buffer.ToString());
            Console.CursorLeft = cli + clOffset + 1;
            key = Console.ReadKey(true);
        }
        else if (key.Key == ConsoleKey.LeftArrow && Console.CursorLeft - clOffset > 0)
        {
            Console.CursorLeft--;
            key = Console.ReadKey(true);
        }
        else if (key.Key == ConsoleKey.RightArrow && Console.CursorLeft - clOffset < buffer.Length)
        {
            Console.CursorLeft++;
            key = Console.ReadKey(true);
        }
        else
        {
            key = Console.ReadKey(true);
        }
    }

    if (key.Key == ConsoleKey.Enter)
    {
        Console.WriteLine();
        line = buffer.ToString();
        return true;
    }
    return false;
}