using System; using System.Collections.Generic; using System.Threading; namespace ProgressBarLib { public class ProgressBar { private volatile bool _shouldStop; private volatile List _msgList = new List(); private volatile List _errList = new List(); private volatile int _lastCurr; private volatile int _lastTotal; private volatile string _lastMsg = ""; private volatile string _mainMsg = ""; private readonly object _lock = new object(); private volatile bool _hasStopped; public ProgressBar() { new Thread(() => { Thread.CurrentThread.IsBackground = true; while (!_shouldStop) { lock (_lock) { if (_msgList.Count > 0) { ClearCurrentConsoleLine(); foreach (var msg in _msgList) { Console.WriteLine(msg.Length < Console.WindowWidth ? msg : msg.Substring(0, Console.WindowWidth - 1)); } _msgList.Clear(); } if (_errList.Count > 0) { ClearCurrentConsoleLine(); foreach (var err in _errList) { Console.WriteLine("ERROR: " + err); } _errList.Clear(); } } ClearCurrentConsoleLine(); Console.Write("\r" + _mainMsg); Thread.Sleep(500); } if (_errList.Count > 0) { ClearCurrentConsoleLine(); foreach (var err in _errList) { Console.WriteLine("ERROR: " + err); } _errList.Clear(); } if (_msgList.Count > 0) { ClearCurrentConsoleLine(); foreach (var msg in _msgList) { Console.WriteLine(msg.Length < Console.WindowWidth ? msg : msg.Substring(0, Console.WindowWidth - 1)); } _msgList.Clear(); } else { ClearCurrentConsoleLine(); Console.Write("\r" + _mainMsg); } _hasStopped = true; }).Start(); } public void UpdateMain(int curr, int total, string msg) { var currstr = curr.ToString(); for (var i = total.ToString().Length - curr.ToString().Length; i > 0; i--) currstr = " " + currstr; UpdateMainAdv(curr, total, currstr, msg); } public void UpdateMainAdv(int curr, int total, string custCurr, string msg) { var outMsg = $"({custCurr}/{total}) "; var progPart = MakeProgressBar(curr, total); var msgMaxLength = Console.WindowWidth - outMsg.Length - progPart.Length; if (msgMaxLength < 0) { _mainMsg = "Increase terminal width"; return; } if (msg.Length < msgMaxLength) for (var i = (msgMaxLength - msg.Length); i > 0; i--) msg += " "; else msg = msg.Substring(0, msgMaxLength); outMsg += msg; outMsg += progPart; _lastCurr = curr; _lastMsg = msg; _lastTotal = total; _mainMsg = outMsg; } public void Tick() { _lastCurr++; UpdateMain(_lastCurr, _lastTotal, _lastMsg); } public void PushMsg(string msg) { lock (_lock) _msgList.Add(msg); } public void PushErr(string err) { lock (_lock) _errList.Add(err); } public void Stop() { _shouldStop = true; while (!_hasStopped) { } } private static string MakeProgressBar(int curr, int total) { if (curr > total) curr = total; const string fullChar = "="; const string blankChar = "-"; var progStr = "["; var fullCharCount = (int) (15d / total * curr); var blankCharCount = 15 - fullCharCount; for (var i = fullCharCount; i > 0; i--) progStr += fullChar; for (var i = blankCharCount; i > 0; i--) progStr += blankChar; progStr += "]"; return progStr; } private static void ClearCurrentConsoleLine() { var currentLineCursor = Console.CursorTop; Console.SetCursorPosition(0, Console.CursorTop); Console.Write(new string(' ', Console.WindowWidth)); Console.SetCursorPosition(0, currentLineCursor); } } }