控制台应用程序 - 当前工作线

时间:2017-02-08 10:50:39

标签: c# console-application

我看过其他一些与此非常相似的帖子,但他们给出的答案并没有正确回答这个问题。对不起,如果有隐藏的东西我找不到......

我想使用Console.WriteLine()打印当前Console.ReadLine()之上的内容,例如,我的应用程序打印“Hello world”并启动一个线程(在5秒内)将打印“我等待5秒“在我需要输入内容的行上方,如下:

Hello world
Please input something: _

然后5秒钟将会过去,它将如下所示:

Hello world
I just waited 5 seconds
Please input something: _

到目前为止,我已尝试使用Console.SetCursorPosition(0,Console.CursorTop - 1),但这只是打印在“请输入内容:_”这一行,如果我使用Console.CursorTop - 2而不是崩溃说“ [2]超出范围“(不知道为什么会这样)并且如果我使用Console.CursorTop - 2它打印在”请输入内容:_“...所以我的问题是我如何打印上面的内容”请输入一些东西:_“

2 个答案:

答案 0 :(得分:5)

只是移动光标不够好,问题是你插入文本。这是可能的,Console.MoveBufferArea()方法允许您访问控制台的底层屏幕缓冲区,并允许您将文本和属性移动到另一行。

有几个棘手的角落案例。你已经找到的,如果光标位于缓冲区的末尾,你必须强制控制台滚动。并且计时器是一个非常难以解决的问题,如果您可以阻止Console.ReadLine()在计时器的Elapsed事件插入文本的同时移动光标,则您只能正确执行此操作。这需要lock,您无法在Console.ReadLine()中插入锁。

您可以使用一些示例代码来实现目标:

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

样本用法:

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

注意Console.Top属性上的测试,它确保当用户键入太多文本并强制滚动或者Console.ReadLine()在计时器选中的同时完成时,没有任何事情发生严重错误。在所有可能的情况下证明它是线程安全的很难做到,当Console.ReadLine()在Elapsed事件处理程序运行的同时水平移动光标时肯定会出现问题。我建议你写your own Console.ReadLine() method,这样你就可以插入锁,并确信它总是安全的。

答案 1 :(得分:0)

您可以使用回车符(\r或U + 000D)将光标返回到当前行的开头,然后覆盖其中的内容。像

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

但是,如果用户已经开始输入,那么这将不再有效(因为您可能无法覆盖他们键入的所有内容,并且他们将丢失他们在屏幕上输入的内容,尽管它仍然在记忆中。

要正确解决此问题,您实际上需要修改控制台的字符缓冲区。您必须将当前行上方的所有内容移动一行,然后插入您的消息。您可以使用Console.MoveBufferArea向上移动区域。然后你需要保存当前光标位置,将光标移动到上面一行的开头,写下你的信息,然后再次将光标位置重置为保存的光标位置。

然后然后你必须希望用户在你写信的时候没有打字,因为这样会搞砸。我不确定您在使用ReadLine时是否可以解决这个问题,因为在ReadLine处于活动状态时您无法暂时锁定某些内容。要正确解决这个问题,您可能必须编写自己的ReadLine替代方案来读取单个按键,并在写入结果字符时锁定公共对象,以避免两个线程同时写入控制台。