How to Update the Current Line in a C# Windows Console App

How can I update the current line in a C# Windows Console App?

If you print only "\r" to the console the cursor goes back to the beginning of the current line and then you can rewrite it. This should do the trick:

for(int i = 0; i < 100; ++i)
{
Console.Write("\r{0}% ", i);
}

Notice the few spaces after the number to make sure that whatever was there before is erased.

Also notice the use of Write() instead of WriteLine() since you don't want to add an "\n" at the end of the line.

How can I update the current line in a C# Windows Console App while waiting for ReadLine?

Yes. You can write to the Console from a separate thread while blocking on Console.ReadLine.

That being said, it's going to cause confusion. In your case, you'll clear out what the user is typing half-way through their line (via Console.Clear()), plus move the cursor position around dramatically.


Edit: Here's an example that shows this:

namespace ConsoleApplication1
{
using System;
using System.Threading;

class Program
{
static void Main(string[] args)
{
Console.WriteLine("Starting");

ThreadPool.QueueUserWorkItem(
cb =>
{
int i = 1;
while (true)
{
Console.WriteLine("Background {0}", i++);
Thread.Sleep(1000);
}
});
Console.WriteLine("Blocking");
Console.WriteLine("Press Enter to exit...");
Console.ReadLine();
}
}
}

If you run this, you'll see the Console waits on ReadLine, but the background thread still prints.

How can I update a line in a C# Windows Console App while without disrupting user input?

I would recommend changing this from a Console application to a Windows application, and putting the reporting into a standard GUI.

This will let you have user input areas of your screen separate from your reporting, and provide a much nicer, more usable interface overall.


That being said, you could potentially position the cursor explicitly, and avoid "interrupting" the user (for longer than your refresh method call). This would require a lot of effort, though.

The keys would be to read the console input key by key (instead of using ReadLine), as you'd need to be able to "retype" the user's previous input.

When it's time to refresh the display, you could get the current cursor position and content for the current line, clear the console, write your new text, write in any text that would have been displaying already (the user's current input), and use Console.SetCursorPosition to explicitly set the cursor location.

If you're going to do that, it might be nice to, up front, set the cursor position to the last line of the console (in effect making a console-mode gui). This would allow you to at least give the effect of an old-school console application, and make this less surprising to the user.

Update specific line on Console

You can use the method Console.SetCursorPosition to set the cursor position and then call Console.Write, that will write starting at the position set.

Something like:

Console.SetCursorPosition(0, 0); // for line 1
Console.Write($"thread 1: {current1} of {total1}");
Console.SetCursorPosition(0, 1); // for line 2
Console.Write($"thread 2: {current2} of {total2}");

Updating multiple line in a console application

The way I prefer to handle progress reporting is to have all worker threads report to a shared progress field, and have a separate timer that reads this fields and reports the progress to the user. This lets me control how often progress is reported, regardless of how fast items are processed. I also want an abstraction layer that allows different ways to report progress. After all, the same method might be used from console, the UI, or not at all.

For example something like this:

public interface IMyProgress
{
void Increment(int incrementValue);
}

public sealed class MyProgress : IMyProgress, IDisposable
{
private readonly int totalItems;
private readonly Timer myTimer;
private volatile int progress;
private int lastUpdatedProgress;
public MyProgress(TimeSpan progressFrequency, int totalItems)
{
this.totalItems = totalItems;
myTimer = new Timer(progressFrequency.TotalMilliseconds);
myTimer.Elapsed += OnElapsed;
myTimer.Start();
}

private void OnElapsed(object sender, ElapsedEventArgs e)
{
var currentProgress = progress;
if (lastUpdatedProgress != currentProgress)
{
lastUpdatedProgress = currentProgress;
var percent = currentProgress * 100 / (double)totalItems;
Console.Write($"\rWork progress: {percent}%");
}
}
public void Increment(int incrementValue) => Interlocked.Add(ref progress, incrementValue);
public void Dispose() => myTimer?.Dispose();
}

This can be called from a parallel method like:

static void Main(string[] args)
{
Console.WriteLine("Hello World!");

var myWorkItems = Enumerable.Range(1, 10000).ToList();
using var progress = new MyProgress(TimeSpan.FromSeconds(1), myWorkItems.Count);
DoProcessing(myWorkItems, progress);
}

private static void DoProcessing(IEnumerable<int> items, IMyProgress progress)
{
Parallel.ForEach(items, item =>
{
Thread.Sleep(20);
progress.Increment(1);
});
}

I would be a bit careful when using carriage returns. In my experience console applications tend to be used by other programs as much as by humans, and then it is likely that the output will be redirected to a file, and that does not support carriage returns. So I would try to make the output look good.

I would avoid trying to move the cursor around. I have tried that, but the result was unsatisfactory, YMMV.

C# - Rewrite/edit line while program is running

It should look like this:

public static void RewriteLine(int lineNumber, String newText)
{
int currentLineCursor = Console.CursorTop;
Console.SetCursorPosition(0, currentLineCursor - lineNumber);
Console.Write(newText); Console.WriteLine(new string(' ', Console.WindowWidth - newText.Length));
Console.SetCursorPosition(0, currentLineCursor);
}

static void Main(string[] args)
{
Console.WriteLine("Text to be rewritten");
Console.WriteLine("Just some text");
RewriteLine(2, "New text");
}

What's happening is that you change cursor position and write there something. You should add some code for handling long strings.

How to modify the previous line of console text?

You can move cursor wherever you want: Console.SetCursorPosition or use Console.CursorTop.

Console.SetCursorPosition(0, Console.CursorTop -1);
Console.WriteLine("Over previous line!!!");


Related Topics



Leave a reply



Submit