【问题标题】:Keep Console.ReadLine always on last line保持 Console.ReadLine 始终在最后一行
【发布时间】:2013-08-05 13:57:46
【问题描述】:

我有一个用 C# 编写的应用程序,它通过 while(true) 循环和 Console.ReadLine 连续接收用户的命令。

我也有通过控制台报告的各种后台操作。查看这个简单的例子:

class Program
{  
        static void Main(string[] args)
        {
            new Messager(1000 * 2.5f);
            new Messager(1000 * 3);
            while (true)
            {
                Console.ReadLine();
            }
        }
}

public class Messager
{
    Timer Timer;

    public Messager(double interval)
    {
        this.Timer = new Timer(interval);
        this.Timer.Elapsed += (sender, e) =>
        {
            Console.WriteLine("message from " + this.Timer.Interval);
        };
        this.Timer.Start();
    }
}

问题是,当我输入命令并且 Messager 在我按下 Enter 之前报告回来时,来自 Messager 的消息将被附加到我的当前行,并且光标将被放置到下一行。看这个截图:

正如你猜想的那样,我不想那样做。我希望命令的当前输入始终保留在最后一行,并将所有消息放在该行上方。正如您对此类应用程序所期望的那样。我如何做到这一点?

【问题讨论】:

  • 我唯一的想法是只在ReadLine() 的末尾接收消息,这可能不是你想要做的,或者你可以用ReadKey() 做一些事情,但这可能会很快就会变得一团糟。我确实认为您可以使用该方法到达您想要的位置。
  • 我不认为可以做到...这一次,Console.ReadLine() 将在 Enter 上滚动,然后用户可以调整窗口大小...
  • 这是一个控制台。您想要的是一个底部带有命令输入区域的输出窗口。完全不是一回事。您可以通过制作一个看起来像这样的 winform / wpf 屏幕来模拟这种外观。

标签: c# multithreading console


【解决方案1】:

这是非常非常难以做到的。您最终会编写自己的控制台输入和输出方法,每个线程都必须调用这些方法。如果任何线程不合作,它就不能正常工作。但可以使用 Console API。

基本思想是让您的通用输出函数写入控制台屏幕缓冲区,并在代码中滚动缓冲区,而不是让文本流到最后一行并自动滚动。我记得,您必须解析换行符和其他控制字符的输出,并正确处理它们。

您也许可以在最后一行使用Console.ReadLine,但这样做可能会在用户输入的文本多于一行的情况下出现问题。此外,用户在行尾按 Enter 可能会导致它向上滚动。在这种情况下,最好使用原始控制台输入。

你会想要非常熟悉Windows consoles

请注意:工作量很大。而且可能不值得付出努力。

【讨论】:

    【解决方案2】:

    这是我的问题的答案:

    class Program
    {
        static void Main(string[] args)
        {
            new Messager(1000 * 2.5f);
            new Messager(1000 * 3);
            while (true)
            {
                Console.ReadLine();
            }
        }
    }
    
    public class Messager
    {
        Timer Timer;
    
        public Messager(double interval)
        {
            this.Timer = new Timer(interval);
            this.Timer.Elapsed += (sender, e) =>
            {
                int currentTopCursor = Console.CursorTop;
                int currentLeftCursor = Console.CursorLeft;
    
                Console.MoveBufferArea(0, currentTopCursor, Console.WindowWidth, 1, 0, currentTopCursor + 1);
    
                Console.CursorTop = currentTopCursor;
    
                Console.CursorLeft = 0;
    
                Console.WriteLine("message from " + this.Timer.Interval);
    
                Console.CursorTop = currentTopCursor +1;
                Console.CursorLeft = currentLeftCursor;
    
            };
            this.Timer.Start();
        }
    }
    

    【讨论】:

      【解决方案3】:

      只是一个想法。您可以使用Console.CursorTopConsole.CursorLeft 属性。

      class Program
      {
          static void Main(string[] args)
          {
              new Messager(1000 * 2.5f);
              new Messager(1000 * 3);
      
              while (true)
              {
                  Console.CursorTop = 0;
                  Console.ReadLine();
              }
          }
      }
      
      public class Messager
      {
          Timer Timer;
      
          static int position = 1;
      
          public Messager(double interval)
          {
              this.Timer = new Timer(interval);
      
              this.Timer.Elapsed += (sender, e) =>
              {
                  lock (this)
                  {
                      var cursorLeft = Console.CursorLeft;
                      Console.CursorLeft = 0;
                      Console.CursorTop = position++;
                      Console.WriteLine("message from " + this.Timer.Interval);
                      Console.CursorTop = 0;
                      Console.CursorLeft = cursorLeft;
                  }
              };
      
              this.Timer.Start();
          }
      }
      

      【讨论】:

      • 这会很快变得复杂。如果position 大于Console.BufferHeight,则会抛出异常。此外,在可见窗口之外书写会滚动文本,或导致文本不可见。然后是写入行长于缓冲区宽度的问题,这将导致意外滚动,并且您的下一个输出行将覆盖以前的文本。您的建议可以在非常有限的情况下工作,但这不是一个好的通用解决方案。
      • 我知道 :) 这是一个快速而肮脏的属性演示。如果他想要一个看起来像 IRC 的输入控制台,他应该考虑一个用户控件
      猜你喜欢
      • 2018-12-15
      • 2013-05-09
      • 2011-02-07
      • 1970-01-01
      • 2015-08-09
      • 1970-01-01
      • 1970-01-01
      • 2022-11-29
      • 2015-08-27
      相关资源
      最近更新 更多