Monday, August 16, 2010

Windows Console progress bar

For pure nostalgic reasons I was looking into some old code. It is a console application, to tweak the UI (or lack of same) I wanted to provide a progress bar in the console window.



Client code for the screenshot above


using System;
using TMData.Console;
using System.Threading;

namespace ConsoleApplication
{
 /// <summary>
 /// Summary description for Class1.
 /// </summary>
 class Class1
 {
  /// <summary>
  /// The main entry point for the application.
  /// </summary>
  [STAThread]
  static void Main(string[] args)
  {
   // Where in the console should the progress bar be located
   ConsoleCoordinate consoleCoordinate = new ConsoleCoordinate(10,2);

   // Instantiate the progress bar
   ConsoleProgressBar consoleProgressBar = new ConsoleProgressBar(StandardHandle.Output, 0, 120, 50, consoleCoordinate);

   int x = 0;
   while (x < 120)
   {
    consoleProgressBar.ShowProgressInPercent(x);
    x++;
    Thread.Sleep(100);
   }
  }
 }
}

Helper classes and structs


using System;
using System.Runtime.InteropServices;

namespace TMData.Console
{
 /// <summary>
 /// Summary description for ConsoleCoordinate.
 /// </summary>
 [StructLayout(LayoutKind.Sequential)]
 public struct ConsoleCoordinate
 {
  private short _x;
  private short _y;

  public ConsoleCoordinate(short x, short y)
  {
   _x = x;
   _y = y;
  }
 }
}

using System;

namespace TMData.Console
{
 public enum StandardHandle
 {
  Input = -10,
  Output = -11,
  Error = -12
 }
}

And finally the main console progress class


using System;
using System.Runtime.InteropServices; 


namespace TMData.Console
{
 /// <summary>
 /// Summary description for ConsoleProgressBar.
 /// </summary>
 public class ConsoleProgressBar
 {
  private float minimumValue = 0;
  private float maximumValue = 0;
  private IntPtr handle;
  private ConsoleCoordinate consoleCoordinate;
  private int progressBarSize = 50;

  public ConsoleProgressBar(StandardHandle handle, int minimumValue, int maximumValue, int progressBarSize, ConsoleCoordinate consoleCoordinate)
  {
   this.handle = GetStdHandle((int)handle);;
   this.minimumValue = (float)minimumValue;
   this.maximumValue = (float)maximumValue;
   this.consoleCoordinate = consoleCoordinate;
  }
  
  public void ShowProgressInPercent (int currentValue)
  {
   int percentComplete =  Convert.ToInt32((currentValue / maximumValue) * 100);
   ShowProgress(percentComplete, percentComplete, "%", "100");
  }

  private void ShowProgress(int currentValue, int percentComplete, string unitIndicator, string maxDisplayValue)
  {
   // Calculations
   int incrementPercent = (int)100.0 / progressBarSize;
   int completeLength = Convert.ToInt32(percentComplete / incrementPercent);

   // Constructing the full string making up the progressbar
   string statusText = currentValue.ToString().PadLeft(maxDisplayValue.Length, ' ') + unitIndicator + " / " + maxDisplayValue + unitIndicator;
   int startPosOfStatusTextInProgressBar = (int)((float)progressBarSize / 2.0 - (int)(statusText.Length / 2.0 ));
   statusText = statusText.PadLeft(startPosOfStatusTextInProgressBar + statusText.Length, ' ');
   statusText = statusText.PadRight(progressBarSize,' ');
   
   // Preparing the completed/incomplete progressbar text string
   string completeText = statusText.Substring(0, completeLength);
   string incompleteText = statusText.Substring(completeText.Length, statusText.Length - completeText.Length);
   bool bResult = SetConsoleCursorPosition(handle, consoleCoordinate);

   // Printing to console and coloring
   SetConsoleTextAttribute(handle, 31);
   System.Console.Write(completeText);
   SetConsoleTextAttribute(handle, 127);
   System.Console.Write(incompleteText);
  }

  #region DllImports
  [DllImport("kernel32.dll")]
  public static extern IntPtr GetStdHandle(int intHandleType);

  [DllImport("kernel32.dll")]
  public static extern bool SetConsoleTextAttribute(IntPtr handle, int wAttributes);

  [DllImport("kernel32.dll", SetLastError=true)]
  public static extern bool SetConsoleCursorPosition(IntPtr handle, ConsoleCoordinate inCoordCharacterAttributes);
  #endregion
 }
}

No comments: