Console Application - WriteLine above current working line

前端 未结 2 824
北荒
北荒 2021-01-18 02:35

I have seen a few other posts very similar to this one, but the answers they give are not correctly answering the question. Sorry if there is something hidden away that I co

相关标签:
2条回答
  • 2021-01-18 03:24

    You can use carriage-return (\r, or U+000D) to return the cursor to the start of the current line, and then overwrite what's there. Something like

    // A bunch of spaces to clear the previous output
    Console.Write("\r                               ");
    Console.WriteLine("\rI just waited 5 seconds");
    Console.Write("Please input something: ");
    

    However, if the user has started typing already this won't work anymore (as you may not overwrite all they have typed, and they will lose what they've typed on the screen, although it's still there in memory.

    To properly solve this you actually need to modify the console's character buffer. You have to move everything above the current line one line up, and then insert your message. You can use Console.MoveBufferArea to move an area up. Then you need to save the current cursor position, move the cursor to the start of the line above, write your message, and reset the cursor position again to the saved one.

    And then you have to hope that the user doesn't type while you're writing your message, because that would mess things up. I'm not sure you can solve that while using ReadLine, though, as you cannot temporarily lock something while ReadLine is active. To properly solve that you may have to write your own ReadLine alternative that reads individual keypresses and will lock on a common object when writing the resulting character to avoid having two threads writing to the console at the same time.

    0 讨论(0)
  • 2021-01-18 03:33

    Just moving the cursor is not good enough, the problem is that you are inserting text. That is possible, the Console.MoveBufferArea() method gives you access to the underlying screen buffer of the console and lets you move text and attributes to another line.

    There are a couple of tricky corner-cases. One you already found, you have to force the console to scroll if the cursor is located at the end of the buffer. And the timer is a very difficult problem to solve, you can only really do this correctly if you can prevent Console.ReadLine() from moving the cursor at the exact same time that the timer's Elapsed event inserts the text. That requires a lock, you cannot insert a lock in Console.ReadLine().

    Some sample code you can play with to get you there:

    static string TimedReadline(string prompt, int seconds) {
        int y = Console.CursorTop;
        // Force a scroll if we're at the end of the buffer
        if (y == Console.BufferHeight - 1) {
            Console.WriteLine();
            Console.SetCursorPosition(0, --y);
        }
        // Setup the timer
        using (var tmr = new System.Timers.Timer(1000 * seconds)) {
            tmr.AutoReset = false;
            tmr.Elapsed += (s, e) => {
                if (Console.CursorTop != y) return;
                int x = Cursor.Left;
                Console.MoveBufferArea(0, y, Console.WindowWidth, 1, 0, y + 1);
                Console.SetCursorPosition(0, y);
                Console.Write("I just waited {0} seconds", seconds);
                Console.SetCursorPosition(x, y + 1);
            };
            tmr.Enabled = true;
            // Write the prompt and obtain the user's input
            Console.Write(prompt);
            return Console.ReadLine();
        }
    }
    

    Sample usage:

    static void Main(string[] args) {
        for (int ix = 0; ix < Console.BufferHeight; ++ix) Console.WriteLine("Hello world");
        var input = TimedReadline("Please input something: ", 2);
    }
    

    Note the test on the Console.Top property, it ensures that nothing goes drastically wrong when the user typed too much text and forced a scroll or if Console.ReadLine() completed at the exact same time that the timer ticked. Proving that it is thread-safe in all possible cases is hard to do, there will surely be trouble when Console.ReadLine() moves the cursor horizontally at the exact same time that the Elapsed event handler runs. I recommend you write your own Console.ReadLine() method so you can insert the lock and feel confident it is always safe.

    0 讨论(0)
提交回复
热议问题