在阅读控制台线时听ESC

时间:2015-08-13 19:08:51

标签: c# console key readline hotkeys

我想将用户输入读入一个字符串,同时仍然可以随时按ESC键,但不定义系统范围的热键。

所以当用户键入e时。 G。 “测试名称”但不是用ENTER确认按ESC,他应该被带回主菜单。

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

这是我能想到的最简单的方法,但由于Console.ReadLine()没有看到ESC,因此无效。

开始输入文字here之前按下时,发现了一种相当复杂的反应方式,但我希望它能随时 。< / p>

6 个答案:

答案 0 :(得分:7)

您可能不得不放弃使用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;
}

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

答案 1 :(得分:3)

Chris Dunaway的one的一点改进版本:

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

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

答案 2 :(得分:0)

这是最简单的方法:

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

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

答案 3 :(得分:0)

如果有人遇到同样的问题。找到了我自己的解决方案。可能会短一点,但至少它可以工作:

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

答案 4 :(得分:0)

嘿,这是我开始继续教育时真正想要答案的问题。我还在学习,所以可能还有很多需要改进的地方,但我想我已经涵盖了这个解决方案的大部分基础知识。

这段代码处理:

  • 写在窗外
  • 在现有文本之后调用方法(在 Console.Write 之后)
  • 调整窗口大小
  • Ctrl 修饰符

这段代码不处理:

  • Tab 键(普通 Console.ReadLine 在超过窗口大小的 Tab 键时有一个错误,因此选择排除它,因为它很少需要......)

我的代码基于 oleg wxChris Dunaway's 的答案,因此也归功于他们。

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

(只需创建一个新的静态类并在其中粘贴以下方法)

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

        while (cki.Key != ConsoleKey.Enter && cki.Key != ConsoleKey.Escape)
        {
            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 == '\0')
                {
                    cki = Console.ReadKey(true);
                    continue;
                }

                Default(ref index, cki, builder);
            }

            cki = Console.ReadKey(true);
        }

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

            isEsc = true;
            return string.Empty;
        }

        isEsc = 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 = YourClassName.CancelableReadLine(out bool isEsc);

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

        //You can also call it after a Console.Write
        Console.WriteLine("\nCalling 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 = YourClassName.CancelableReadLine(out bool isEsc);

        if (isEsc) 
            return;

答案 5 :(得分:0)

稍微改进的 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;
}