Console application: How to update the display without flicker?

ぐ巨炮叔叔 提交于 2020-12-02 05:53:25

问题


Using C# 4 in a Windows console application that continually reports progress how can I make the "redraw" of the screen more fluid?

I'd like to do one of the following: - Have it only "redraw" the part of the screen that's changing (the progress portion) and leave the rest as is. - "Redraw" the whole screen but not have it flicker.

Currently I re-write all the text (application name, etc.). Like this:

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")));

Which causes a lot of flickering.


回答1:


Try Console.SetCursorPosition. More details here: How can I update the current line in a C# Windows Console App?




回答2:


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



回答3:


I know this question is a bit old but I found if you set Console.CursorVisible = false then the flickering stops as well.




回答4:


Here's a simple working demo that shows multi-line usage without flickering. It shows the current time and a random string every second.

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

The above example assumes your console window is blank to start. If not, make sure to use Console.Clear() first.

Technical Note: SetCursorPosition(0,0) places the cursor back to the top (0,0) so the next call to Console.Write will start from line 0, char 0. Note, it doesn't delete the previous content before writing. As an example, if you write "asdf" over a previous line such as "0123456", you'll end up with something like "asdf456" on that line. For that reason, we use a whiteSpace variable to ensure any lingering characters from the previous line are overwritten with blank spaces. Adjust the length of the whiteSpace variable to meet your needs. You only need the whiteSpace variable for lines that change.

Personal Note: For my purposes, I wanted to show the applications current status (once a second) along with a bunch of other status information and I wanted to avoid any annoying flickering that can happen when you use Console.Clear(). In my application, I run my status updates behind a separate thread so it constantly provides updates even though I have numerous other threads and long running tasks going at the same time.

Credits: Thanks to previous posters and dtb for the random string generator used in the demo. How can I generate random alphanumeric strings in C#?




回答5:


You could try to hack something together using the core libraries.

Rather than waste your time for sub-standard results, I would check out this C# port of the ncurses library (which is a library used for formatting console output):

Curses Sharp




回答6:


I think you can use \r in Windows console to return the beginning of a line. You could also use SetCursorPosition.




回答7:


I would recommend the following extension methods. They allow you to use a StringBuilder to refresh the console view without any flicker, and also tidies up any residual characters on each line

The Problem: The following demo demonstrates using a standard StringBuilder, where updating lines that are shorter than the previously written line get jumbled up. It does this by writing a short string, then a long string on a loop:

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

Result:

The Solution: By using the extension method provided below, the issue is resolved

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

Result:

Extension Methods:

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



回答8:


I actually had this issue so I made a quick simple method to try and eliminate this.

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

It actually worked surprisingly well and I hope it may work for you!



来源:https://stackoverflow.com/questions/5435460/console-application-how-to-update-the-display-without-flicker

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!