Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Console progress bar. Code is under the MIT License: http://opensource.org/licenses/MIT
using System;
using System.Threading;
static class Program {
static void Main() {
Console.Write("Performing some task... ");
using (var progress = new ProgressBar()) {
for (int i = 0; i <= 100; i++) {
progress.Report((double) i / 100);
Thread.Sleep(20);
}
}
Console.WriteLine("Done.");
}
}
using System;
using System.Text;
using System.Threading;
/// <summary>
/// An ASCII progress bar
/// </summary>
public class ProgressBar : IDisposable, IProgress<double> {
private const int blockCount = 10;
private readonly TimeSpan animationInterval = TimeSpan.FromSeconds(1.0 / 8);
private const string animation = @"|/-\";
private readonly Timer timer;
private double currentProgress = 0;
private string currentText = string.Empty;
private bool disposed = false;
private int animationIndex = 0;
public ProgressBar() {
timer = new Timer(TimerHandler);
// A progress bar is only for temporary display in a console window.
// If the console output is redirected to a file, draw nothing.
// Otherwise, we'll end up with a lot of garbage in the target file.
if (!Console.IsOutputRedirected) {
ResetTimer();
}
}
public void Report(double value) {
// Make sure value is in [0..1] range
value = Math.Max(0, Math.Min(1, value));
Interlocked.Exchange(ref currentProgress, value);
}
private void TimerHandler(object state) {
lock (timer) {
if (disposed) return;
int progressBlockCount = (int) (currentProgress * blockCount);
int percent = (int) (currentProgress * 100);
string text = string.Format("[{0}{1}] {2,3}% {3}",
new string('#', progressBlockCount), new string('-', blockCount - progressBlockCount),
percent,
animation[animationIndex++ % animation.Length]);
UpdateText(text);
ResetTimer();
}
}
private void UpdateText(string text) {
// Get length of common portion
int commonPrefixLength = 0;
int commonLength = Math.Min(currentText.Length, text.Length);
while (commonPrefixLength < commonLength && text[commonPrefixLength] == currentText[commonPrefixLength]) {
commonPrefixLength++;
}
// Backtrack to the first differing character
StringBuilder outputBuilder = new StringBuilder();
outputBuilder.Append('\b', currentText.Length - commonPrefixLength);
// Output new suffix
outputBuilder.Append(text.Substring(commonPrefixLength));
// If the new text is shorter than the old one: delete overlapping characters
int overlapCount = currentText.Length - text.Length;
if (overlapCount > 0) {
outputBuilder.Append(' ', overlapCount);
outputBuilder.Append('\b', overlapCount);
}
Console.Write(outputBuilder);
currentText = text;
}
private void ResetTimer() {
timer.Change(animationInterval, TimeSpan.FromMilliseconds(-1));
}
public void Dispose() {
lock (timer) {
disposed = true;
UpdateText(string.Empty);
}
}
}
@belczyk

This comment has been minimized.

Copy link

@belczyk belczyk commented Oct 21, 2015

Awesome stuff 👍

@traktraktrugui

This comment has been minimized.

Copy link

@traktraktrugui traktraktrugui commented May 7, 2016

👏 It's exactly what I was searching for!

@affans

This comment has been minimized.

Copy link

@affans affans commented Jul 21, 2016

This dosnt work on .net core 1.0. Something to do with the new Timer(...) error

@chuongtv

This comment has been minimized.

Copy link

@chuongtv chuongtv commented Aug 10, 2016

Awesome. thanks you!

@nidhind

This comment has been minimized.

Copy link

@nidhind nidhind commented Aug 24, 2016

👏 It's exactly what I was searching for!

@vdovinanton

This comment has been minimized.

Copy link

@vdovinanton vdovinanton commented Sep 1, 2016

thx for the stuff ✌️

@ernest-galbrun

This comment has been minimized.

Copy link

@ernest-galbrun ernest-galbrun commented Sep 28, 2016

Great thank you for sharing this :D

@Pyropace

This comment has been minimized.

Copy link

@Pyropace Pyropace commented Oct 14, 2016

awesome , how about a panel ?

@ElPumpo

This comment has been minimized.

Copy link

@ElPumpo ElPumpo commented Oct 23, 2016

Awesome!

@ToineSeiter

This comment has been minimized.

Copy link

@ToineSeiter ToineSeiter commented Nov 16, 2016

Great job !

@khaledtamam

This comment has been minimized.

Copy link

@khaledtamam khaledtamam commented Dec 21, 2016

thanks

@gabernardone

This comment has been minimized.

Copy link

@gabernardone gabernardone commented Mar 23, 2017

Awesome!

@andreae75

This comment has been minimized.

Copy link

@andreae75 andreae75 commented May 8, 2017

Great Job, Thanks !

@raffacabofrio

This comment has been minimized.

Copy link

@raffacabofrio raffacabofrio commented May 18, 2017

awesome. Thanks for sharing.

@badmotorfinger

This comment has been minimized.

Copy link

@badmotorfinger badmotorfinger commented Jun 15, 2017

What about multiple async progress bars in the console?

@Premx

This comment has been minimized.

Copy link

@Premx Premx commented Jun 16, 2017

I'm just here for the animation at the end uwu

@tranquilChaos

This comment has been minimized.

Copy link

@tranquilChaos tranquilChaos commented Aug 25, 2017

Very nice and thanks for sharing!

Timer doesn't work under .Net Core. To make work, change it to:

timer = new Timer(TimerHandler, new AutoResetEvent(false), TimeSpan.FromSeconds(1.0 / 8), TimeSpan.FromSeconds(1.0 / 8));

@AndreyWeber

This comment has been minimized.

Copy link

@AndreyWeber AndreyWeber commented Dec 6, 2017

Great! Thank you!

@yang-xiaodong

This comment has been minimized.

Copy link

@yang-xiaodong yang-xiaodong commented Jan 10, 2018

Great, Thanks!

@snoophogg

This comment has been minimized.

Copy link

@snoophogg snoophogg commented Apr 6, 2018

Thanks for this, it's great, I added a function to allow writes to console:

	public void WriteLine(string text) {
		lock (timer) {
			UpdateText(string.Empty);
			Console.WriteLine(text);
			UpdateText(currentText);
		}
	}
@jo15765

This comment has been minimized.

Copy link

@jo15765 jo15765 commented Aug 24, 2018

I am downloading a file via sftp and a C# console app. How can I use this to display a progress bar to the user in the console app? I do not understand how it reports progress???

@ShujaathKhan

This comment has been minimized.

Copy link

@ShujaathKhan ShujaathKhan commented Dec 20, 2018

Beautiful, works like charm.

@GeraldAburto

This comment has been minimized.

Copy link

@GeraldAburto GeraldAburto commented Jan 18, 2019

Love it!

@ekaraman89

This comment has been minimized.

Copy link

@ekaraman89 ekaraman89 commented Mar 3, 2019

excellent, thank you so much

@RanderThe

This comment has been minimized.

Copy link

@RanderThe RanderThe commented Mar 14, 2019

Amazing ! Thank you!

@jayprakashbirla

This comment has been minimized.

Copy link

@jayprakashbirla jayprakashbirla commented Mar 27, 2019

thanks you

@ssinfod

This comment has been minimized.

Copy link

@ssinfod ssinfod commented Apr 4, 2019

Hello, I have a question.
Since the Timer implements IDisposable, should you not call Dispose() on the timer ?

public void Dispose() {
    lock (timer) {
        disposed = true;
        UpdateText(string.Empty);
        timer.Dispose(); // Something like that...
    }
}

Or it it done by setting disposed = true ?

@petervanleeuwen

This comment has been minimized.

Copy link

@petervanleeuwen petervanleeuwen commented Jun 10, 2019

Does this work with console logging? Or do I lose them...

@DanielSWolf

This comment has been minimized.

Copy link
Owner Author

@DanielSWolf DanielSWolf commented Aug 14, 2019

Hello, I have a question.
Since the Timer implements IDisposable, should you not call Dispose() on the timer ?

@ssinfod Sorry for the late reply. Yes, disposing the timer after the lock block would certainly be good style because it would free the timer's resources immediately rather than on garbage collection. On the other hand, it doesn't impact behavior, since the timer fires at most one more time, which is ignored due to the disposed flag which is checked within TimerHandler.

@DanielSWolf

This comment has been minimized.

Copy link
Owner Author

@DanielSWolf DanielSWolf commented Aug 14, 2019

@DanielSWolf StackOverflow Question

I've added an answer to your SO question. I hope that explains it.

@DeusLoVults

This comment has been minimized.

Copy link

@DeusLoVults DeusLoVults commented Aug 25, 2019

Noice.

@Akiriki

This comment has been minimized.

Copy link

@Akiriki Akiriki commented Sep 11, 2019

That's perfect and saving me a lot of time, thank you!!

@americofreitasjr

This comment has been minimized.

Copy link

@americofreitasjr americofreitasjr commented Sep 20, 2019

Awesome!

@phsantiago32

This comment has been minimized.

Copy link

@phsantiago32 phsantiago32 commented Nov 22, 2019

Awesome, thanks for sharing!

@ahmedwadod

This comment has been minimized.

Copy link

@ahmedwadod ahmedwadod commented Dec 23, 2019

Man I loved it. That's so dope!

@omersavas

This comment has been minimized.

Copy link

@omersavas omersavas commented Jan 30, 2020

Thanks!

@demlol5

This comment has been minimized.

Copy link

@demlol5 demlol5 commented Apr 7, 2020

awesome!

@StavoG

This comment has been minimized.

Copy link

@StavoG StavoG commented Apr 23, 2020

Awesome stuff!!! Thank you

@hepe00

This comment has been minimized.

Copy link

@hepe00 hepe00 commented Jun 3, 2020

In production code, it only go to 90% oftenly , how can i fix it.

@hepe00

This comment has been minimized.

Copy link

@hepe00 hepe00 commented Jun 3, 2020

Thanks a lot.

@DanielSWolf

This comment has been minimized.

Copy link
Owner Author

@DanielSWolf DanielSWolf commented Jun 3, 2020

In production code, it only go to 90% oftenly , how can i fix it.

Check that you actually report a value larger than 0.9. Also, do you dispose the progress bar (either by calling dispose or via using), as shown in the example?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment