控制台应用程序:如何在没有闪烁的情况下更新显示器?

时间:2011-03-25 16:44:40

标签: c#-4.0 console

在不断报告进度的Windows控制台应用程序中使用C#4如何使屏幕的“重绘”更加流畅?

我想做以下其中一项:   - 让它只“重绘”正在改变的屏幕部分(进度部分),并保持原样。   - “重绘”整个屏幕,但没有闪烁。

目前我重写了所有文本(应用程序名称等)。像这样:

Console.Clear();
WriteTitle();
Console.WriteLine();
Console.WriteLine("Deleting:\t{0} of {1} ({2})".FormatString(count.ToString("N0"), total.ToString("N0"), (count / (decimal)total).ToString("P2")));

导致大量闪烁。

8 个答案:

答案 0 :(得分:15)

试试Console.SetCursorPosition。更多详情:How can I update the current line in a C# Windows Console App?

答案 1 :(得分:6)

static void Main(string[] args)
{
    Console.SetCursorPosition(0, 0);
    Console.Write("################################");
    for (int row = 1; row < 10; row++)
    {
        Console.SetCursorPosition(0, row);
        Console.Write("#                              #");
    }
    Console.SetCursorPosition(0, 10);
    Console.Write("################################");

    int data = 1;
    System.Diagnostics.Stopwatch clock = new System.Diagnostics.Stopwatch();
    clock.Start();
    while (true)
    {
        data++;
        Console.SetCursorPosition(1, 2);
        Console.Write("Current Value: " + data.ToString());
        Console.SetCursorPosition(1, 3);
        Console.Write("Running Time: " + clock.Elapsed.TotalSeconds.ToString());
        Thread.Sleep(1000);
    }

    Console.ReadKey();
}

答案 2 :(得分:5)

我知道这个问题有点陈旧但我发现如果你设置Console.CursorVisible = false,那么闪烁也会停止。

答案 3 :(得分:0)

您可以尝试使用核心库一起破解某些内容。

我会检查ncurses库的这个C#端口(这是一个用于格式化控制台输出的库),而不是浪费你的时间用于不合标准的结果:

Curses Sharp

答案 4 :(得分:0)

我认为您可以在Windows控制台中使用\ r来返回行的开头。 您也可以使用SetCursorPosition。

答案 5 :(得分:0)

这是一个简单的工作演示,显示多行使用而不会闪烁。它每秒显示当前时间和随机字符串。

    private static void StatusUpdate()
    {
        var whiteSpace = new StringBuilder();
        whiteSpace.Append(' ', 10);
        var random = new Random();
        const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        var randomWord = new string(Enumerable.Repeat(chars, random.Next(10)).Select(s => s[random.Next(s.Length)]).ToArray());
        while (true)
        {
            Console.SetCursorPosition(0, 0);
            var sb = new StringBuilder();
            sb.AppendLine($"Program Status:{whiteSpace}");
            sb.AppendLine("-------------------------------");
            sb.AppendLine($"Last Updated: {DateTime.Now}{whiteSpace}");
            sb.AppendLine($"Random Word: {randomWord}{whiteSpace}");
            sb.AppendLine("-------------------------------");
            Console.Write(sb);
            Thread.Sleep(1000);
        }
    }

上面的示例假设您的控制台窗口为空,以便启动。如果没有,请确保首先使用Console.Clear()。

技术说明: SetCursorPosition(0,0)将光标放回到顶部(0,0),因此下一次调用Console.Write将从第0行开始,char 0.注意,它不会在写入之前删除以前的内容。例如,如果你在前一行(如“0123456”)上写“asdf”,那么你最终会在该行上输入类似“asdf456”的内容。因此,我们使用whiteSpace变量来确保前一行中的任何延迟字符都被空格覆盖。调整whiteSpace变量的长度以满足您的需要。对于更改的行,您只需要whiteSpace变量。

个人注意事项 出于我的目的,我想显示应用程序的当前状态(每秒一次)以及一堆其他状态信息,我想避免在使用Console.Clear()时可能发生的任何烦人的闪烁。在我的应用程序中,我在一个单独的线程后面运行我的状态更新,因此它会不断提供更新,即使我有许多其他线程和长时间运行的任务同时进行。

<强>现金: 感谢以前的海报和dtb,用于演示中使用的随机字符串生成器。 How can I generate random alphanumeric strings in C#?

答案 6 :(得分:0)

我建议以下扩展方法。它们使您可以使用StringBuilder刷新控制台视图而没有任何闪烁,并且还可以整理每行上的所有残留字符

问题:以下演示演示了如何使用标准的StringBuilder,其中比以前编写的行短的更新行变得混乱。它是通过先写一个短字符串,然后在循环上写一个长字符串来实现的:

public static void Main(string[] args)
{            
   var switchTextLength = false;

   while(true)
   {
       var sb = new StringBuilder();       

       if (switchTextLength)
            sb.AppendLine("Short msg");
       else
            sb.AppendLine("Longer message");

       sb.UpdateConsole();

       switchTextLength = !switchTextLength;
       Thread.Sleep(500);
   }
}

结果:

The remainder of the longer string still exists because the shorter message is not long enough to overwrite it

解决方案:通过使用下面提供的扩展方法,问题得以解决

public static void Main(string[] args)
{            
   var switchTextLength = false;

   while(true)
   {
       var sb = new StringBuilder();       

       if (switchTextLength)
            sb.AppendLineEx("Short msg");
       else
            sb.AppendLineEx("Longer message");

       sb.UpdateConsole();

       switchTextLength = !switchTextLength;
       Thread.Sleep(500);
   }
}

结果:

The shorter line is written out, and the rest of the buffer space is overwritten with empty space

扩展方法:

public static class StringBuilderExtensions
{
    /// <summary>
    /// Allows StrinbBuilder callers to append a line and blank out the remaining characters for the length of the console buffer width
    /// </summary>
    public static void AppendLineEx(this StringBuilder c, string msg)
    {
        // Append the actual line
        c.Append(msg);
        // Add blanking chars for the rest of the buffer
        c.Append(' ', Console.BufferWidth - msg.Length - 1);
        // Finish the line
        c.Append(Environment.NewLine);
    }

    /// <summary>
    /// Combines two StringBuilders using AppendLineEx
    /// </summary>
    public static void AppendEx(this StringBuilder c, StringBuilder toAdd)
    {
        foreach (var line in toAdd.ReadLines())
        {
            c.AppendLineEx(line);
        }
    }

    /// <summary>
    /// Hides the console cursor, resets its position and writes out the string builder
    /// </summary>
    public static void UpdateConsole(this StringBuilder c)
    {
        // Ensure the cursor is hidden
        if (Console.CursorVisible) Console.CursorVisible = false;

        // Reset the cursor position to the top of the console and write out the string builder
        Console.SetCursorPosition(0, 0);
        Console.WriteLine(c);
    }
}

答案 7 :(得分:0)

我实际上遇到了这个问题,所以我做了一个快速简单的方法来尝试消除这个问题。

    static void Clear(string text, int x, int y)
    {
        char[] textChars = text.ToCharArray();
        string newText = "";
        //Converts the string you just wrote into a blank string
        foreach(char c in textChars)
        {
            text = text.Replace(c, ' ');
        }
        
        newText = text;

        //Sets the cursor position
        Console.SetCursorPosition(x, y);

        //Writes the blank string over the old string
        Console.WriteLine(newText);

        //Resets cursor position
        Console.SetCursorPosition(0, 0);
    }

实际上效果很好,我希望它可能对您有用!