Page 1 of 1

Progress Bars in Command Prompt - Part B A thorough look into what goes into a command line progress bar

#1 SwiftStriker00  Icon User is offline

  • Microsoft Insider
  • member icon

Reputation: 429
  • View blog
  • Posts: 1,596
  • Joined: 25-December 08

Posted 29 December 2009 - 08:15 AM

You will see that the code will compensate for that, even when the user puts in the the desired length they will have to realize that. This is how i persevered the code, and your welcome to change it in your implementation.


Properties (Getters/Setters)
Now one of my favorite features of C# is the ability to create properties, which are fancy getter and setters for the class variables. It also cuts down on all the () you have to type. I also think its more appropriate for some of the basic derived variables to be referenced as a property instead of having to make a computeVarXYZ() method. However I will show you a comparison if the syntax confuses you:
The first variable we will work with is progress
  public double Progress {
   get { return progress; }
   set { progress = value; }
  }


Is essentially the same as:
public double getProgress(){
 return progress;
}
public void setProgress( double p ){
 progress = p;
}


However the difference is the way you use them:
ProgressBar a = new ProgressBar();
Console.WriteLine( "I am step " + a.Progress + " out of " + a.Total );
a.Progress += 2;
Console.WriteLine( "I am step " + a.Progress + " out of " + a.Total );

ProgressBar b = new ProgressBar();
Console.WriteLine( "I am step " + b.getProgress() + " out of " + b.getTotal() );
b.setProgress( b.getProgress() + 2 );
Console.WriteLine( "I am step " + b.getProgress() + " out of " + b.getTotal() );




So using properties I will make one for each of the variables, however for now the user will not be able to modify the total and the size. I didn't deem it viable for the client to be able to access those, so they will only have accessors and no mutators.

  public double Progress {
   get { return progress; }
   set { progress = value; }
  }

  public double Total {
   get { return total; }
  }

  public int Size {
   get { return size; }
  }




Now what of PercentDone you say? well remember were not going to bother saving it as an actual variable because its always going to have to be changed, but we still want to be able to access that value from time to time so we do the following:
  public double PercentDone {
   get { return progress / total; }
   set { progress = total * value; }
  }


Here I am using simple math based off the knowledge that the percent done is a relationship between the progress and the total. If we want to get the percent we just divide progress over total. I added the setter for more functionality to the program, but since we never store percentDone we have to save it in terms of the progress, which is the percent times the total.

The last piece we need to actually display an accurate progress bar is how many of the marks should be shown as completed or empty, for example:
40% of size 10 will be: ****------
73% of size 10 will be: *******---
Well if you remember this x / size = progress / total then all we need to do is put that into code! I will call x 'Distance' from now on, since its the distance out of the size that needs to be filled in.
  public int Distance {
   get { return (int)Math.Floor( ((progress * ((double)size)) / total) ); }
  }


A quick explanation of the code, first we have to cast size into a double because of type checking (keeping the precision that we want), and you can see that x or Distance is progress*size/total because of the cross multiplication discussed earlier. Next we take that result and take the floor value of it. Floor in math means get me the highest reached whole integer so 9.0 == Math.Floor(9.0) and 9.0 == Math.Floor(9.99999) . I do this because as stated before we cannot display fractions of ascii characters in the cmd prompt. That will give us last completed mark, then lastly we cast it into an int (which means it will just truncate the .0 from 9.0 which we didn't need anyway). We have completed our proportion, and now we can display our ProgressBar.

Display
I will show you two ways of showing off your progress bar. One simple way and the second way to make it look like the bar is actually changing in the command prompt.

The simple was is to override the toString() method. We can return the entire bar as 1 string.
  public override string ToString() {
   string returnString = "[";
   for (int i = 0; i < this.Distance; i++) {
	returnString += "*";
   }
   int theRest = this.Size - this.Distance;
   for (int i = 0; i < theRest; i++) {
	returnString += "-";
   }
   returnString += "]";
   return returnString;
  }



This is pretty easy to follow since we took time to define good properties above. First in the returnString I will add one of my caps, for aesthetic reasons. Then we need to display how far along we are (visually) which we defined as Distance so we loop through and append an '*'. Then for the rest of the bar we want to show as empty. We quickly evaluated the difference and loop through and append a '-' to the end of the bar. Finally appending the closing cap and returning the string. The result will look something like this [*****--------]. To test this you can go into your client or main method and do the following:
 ProgressBar s = new ProgressBar( 2, 10, 12 );
 Console.WriteLine( s );



Now lets add some colors to the output, since it will be a pain from the client to break apart the string and color pieces as it goes, we will create a report() method in the ProgressBar class. Since we have color representation now, we can use the same character (I'm going to stick with '*'). Our wonderful .Net will let us color only pieces of the output, versus having to type the command "color 21" into our window which we just change the window settings.
public void report() {
   ConsoleColor fore = Console.ForegroundColor,
	   back = Console.BackgroundColor;

  Console.ForegroundColor = ConsoleColor.DarkRed;
   Console.Write("[");
   Console.ForegroundColor = ConsoleColor.Green;
   for (int i = 0; i < this.Distance; i++) {
	Console.Write( "*" );
   }
   
   int theRest = this.Size - this.Distance;
   Console.ForegroundColor = ConsoleColor.DarkGray;
   for (int i = 0; i < theRest; i++) {
	Console.Write( "*" );
   }
   Console.ForegroundColor = ConsoleColor.DarkRed;
   Console.Write("]");
  Console.ForegroundColor = fore;

  }



In this code we change the foreground color write the characters needed, then change the foreground color again and finally setting it back to default. Now it is arguable that the Console.Write("]"); line can be changed to Console.WriteLine("]"); however we are going to leave it as just Write() to add some cool functionality.

Right now in our client if we made a loop to show the progression of the progress bar from 0 to 100%, 10% at a time the code and output will look like this:
public static void Main( string[] args ) {
 ProgressBar b = new ProgressBar( 200, 12 );

 b.report();
 Console.WriteLine();
 while (b.PercentDone != 1) { //Keep going to 100%
  b.PercentDone += .1;
  wait( 100000000 );
 b.report();
 Console.WriteLine();
 }
}

public static void wait( int tiem ) {
 for (int i = 0; i < tiem; i++) {
 //Make the comp do some argueous task to make it seem as
 //if it is pausing
 }
}


your output will be:

[**********]
[**********]
[**********]
[**********]
[**********]
[**********]
[**********]
[**********]
[**********]
[**********]
[**********]


Which is cool, our code will definitely will show you the progression, however wouldn't it be cool if there was only 1 bar that changed? As if it where a graphical bar? If the answer is yes, read on.

First we need to add another method to our ProgressBar class called update(). This will allow us simulate that the bar is being updated in lou of just printed again.
public void update() {
   Console.CursorLeft -=(this.Size+2);
   report();
  }


CursorLeft is the property of the console of its current position in the current line's buffer in the command prompt. What we want to do is slide it back to the beginning of the buffer so we can overwrite what was just displayed (subtract from CursorLeft will move the position left, adding will move it right).

Secondly we have to make some modifications to our client (and I'm not going to bother displaying the wait() definition since it is unchanged).
public static void Main( string[] args ) {
   ProgressBar b = new ProgressBar( 200 );
   Console.BufferWidth = 81;

   b.report();
   while (b.PercentDone != 1) {
	b.PercentDone += .1;
	b.update();
	wait( 100000000 );

   }
   Console.BufferWidth = 80;

  }


Now in the loop instead of reporting it will call update instead. However we need to modify the buffer width, because by default the width is the size of the prompt and in my example I will use all 80 characters to display my progress bar. The reason we have to increase the BufferWidth, is because if we don't, after the last character printed in the buffer (and its completely filled), it will return down to a new line. This is bad because if we tell the Console to shift back 80 characters from 0 it will blow up. so we add 1 to makes sure we are always shifting back on the same line.

Now your progress bar will look as if it is changing its color as it progresses.!


Thank you for reading my tutorial, I make these to give everyone an opportunity to do what they want in programming. And you will always excel with a strong knowledge of the basics.

Attached are copies of my client and ProgressBar files for you to look at in their entirety.

Is This A Good Question/Topic? 1
  • +

Page 1 of 1