5

让我解释一下,这就是我想要实现的目标:

Enter name:
F4-Quit

有没有一种方法可以显示下一行(F4-Quit)而无需 readline 在显示之前等待用户输入?原因是我想随时退出,即使尚未输入姓名(光标正在等待),这在许多情况下非常有用,即有人在输入信息的过程中改变主意并想退出或返回.

如果那是不可能的,那么解决方法是什么?

谢谢你!

4

5 回答 5

8

只需编写您自己的 ReadLine() 版本。这是 .NET 的 TryParse() 模式中的 TryReadLine() 版本:

    static bool TryReadLine(out string result) {
        var buf = new StringBuilder();
        for (; ; ) {
            var key = Console.ReadKey(true);
            if (key.Key == ConsoleKey.F4) {
                result = "";
                return false;
            }
            else if (key.Key == ConsoleKey.Enter) {
                result = buf.ToString();
                return true;
            }
            else if (key.Key == ConsoleKey.Backspace && buf.Length > 0) {
                buf.Remove(buf.Length - 1, 1);
                Console.Write("\b \b");
            }
            else if (key.KeyChar != 0) {
                buf.Append(key.KeyChar);
                Console.Write(key.KeyChar);
            }
        }
    }
于 2013-11-01T20:44:21.957 回答
4

如果那是不可能的,那么解决方法是什么?

解决此问题的最佳方法是使用 GUI 应用程序。这将消除控制台对您的限制。

一般来说,如果你想要复杂的用户输入控制,UI 会比控制台更好。

如果您必须使用控制台应用程序,您可能需要Console.ReadKey在循环中使用而不是Console.ReadLine,并检查“escape”键击。一旦输入了换行符,您就可以处理您的“文本”命令/输入/等。

于 2013-11-01T20:06:44.530 回答
3

这是 Jeppe 指出的解决方案

 public static void Main()
    {
        ConsoleKeyInfo cki;

        Console.Clear();

        // Establish an event handler to process key press events.
         Console.CancelKeyPress += new ConsoleCancelEventHandler(myHandler);

        while (true) {
            Console.Write("Press any key, or 'X' to quit, or ");
            Console.WriteLine("CTRL+C to interrupt the read operation:");

            // Start a console read operation. Do not display the input.
            cki = Console.ReadKey(true);

            // this process can be skipped 
            // Console.WriteLine("  Key pressed: {0}\n", cki.Key);

            // Exit if the user pressed the 'X' key. 
            if (cki.Key == ConsoleKey.F4) break;
       }
}
protected static void myHandler(object sender, ConsoleCancelEventArgs args)
{
            args.Cancel = true;
            go_back_to_main();
 }
于 2013-11-01T20:49:11.183 回答
2

如果它不必是 F4 键,但您可以使用 Ctrl+C 或 Ctrl+Break,那么您也许可以使用CancelKeyPressevent

于 2013-11-01T20:22:44.527 回答
1

所以你想做件事:

1

对于第一个问题,您正在寻找的似乎是 Console.SetCursorPosition()。使用 SetCursorPosition,您可以跳转到控制台窗口中的任何位置。在您的情况下,您想从您所在的位置和控制台跳到下一行。写下您的“F4-Quit”,然后再次跳回。

Console.CursorTop  //Gets and sets the current Console row position
Console.CursorLeft //Gets and sets the current Console column position

//To get to next row from where you currently are:
Console.SetCursorPosition(Console.CursorLeft, Console.CursorTop + 1);
    //Or:
    Console.CursorTop++;

因此,您的潜在解决方案是:

Console.Write("Enter Name: ");
var startPos = new int[] { Console.CursorLeft, Console.CursorTop};

Console.CursorLeft = 0;
Console.CursorTop++;
Console.Write("F4-Quit");

Console.SetCursorPosition(startPos[0], startPos[1]);
Console.ReadLine();

2

至于取消 Console.ReadLine() 您需要更多代码:

正如Hans Passant指出的那样,您可以创建自己的 Console.ReadLine 但这可能非常耗时。因此,如果没有其他更好的方法,我制作的方法似乎可以解决问题:

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

public static class XConsole
{

}

并在其中粘贴以下方法:

    public static string CancelableReadLine(out bool isCancelled)
    {
        var cancelChar = ConsoleKey.F4; // You can change the character for cancelling here if you want
        var builder = new StringBuilder();
        var cki = Console.ReadKey(true);
        int index = 0;
        (int left, int top) startPosition;

        while (cki.Key != ConsoleKey.Enter && cki.Key != cancelChar)
        {
            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 == cancelChar)
        {
            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;

此代码处理:

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

此代码不处理:

  • 选项卡键(正常控制台。阅读线在比窗口大小更远的情况下有一个错误,因此选择将其排除在很少需要的情况下...)

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

我建议尝试根据这些答案构建自己的,但有时您只需要一些工作即可。

希望这可以帮助!

参考:

https://stackoverflow.com/a/66495807/15337673

于 2021-09-22T13:16:54.540 回答