using System; using System.Linq; using System.Text; using System.Threading; namespace Sony.Filtr.Utility.ConsoleUtility { public class ConsoleProgressBarCount : IDisposable, IProgress { private readonly int _totalCount; private const int _BlockCount = 50; private readonly TimeSpan _animationInterval = TimeSpan.FromSeconds(1.0 / 8); private const string _Animation = @"|/-\"; private readonly Timer _timer; private int _currentProgress; private string _currentText = string.Empty; private bool _disposed; private int _animationIndex; public ConsoleProgressBarCount(int totalCount) { _totalCount = totalCount; _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 (!System.Console.IsOutputRedirected) { ResetTimer(); } } public void Report(int processed) { Interlocked.Add(ref _currentProgress, processed); } private void TimerHandler(object state) { lock (_timer) { if (_disposed) return; var percentageProgress = _currentProgress / (double)_totalCount; var progressBlockCount = (int)(percentageProgress * _BlockCount); var percent = (int)(percentageProgress * 100); var 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); } } } }