(C) @DanielSWolf 2015
Console progress bar. Code is under the MIT License: http://opensource.org/licenses/MIT
Translation to Visual Basic .NET by @vain0 is under the Public Domain.
(C) @DanielSWolf 2015
Console progress bar. Code is under the MIT License: http://opensource.org/licenses/MIT
Translation to Visual Basic .NET by @vain0 is under the Public Domain.
Imports System | |
Imports System.Threading | |
Module Program | |
Public Sub Main() | |
Console.Write("Performing some task... ") | |
Using bar = New ProgressBar | |
For i = 0 To 100 | |
bar.Report(i / 100) | |
Thread.Sleep(20) | |
Next | |
End Using | |
Console.WriteLine("Done.") | |
End Sub | |
End Module |
Imports System | |
Imports System.Text | |
Imports System.Threading | |
Public Class ProgressBar | |
Implements IDisposable | |
Implements IProgress(Of Double) | |
Private ReadOnly _animationInterval As TimeSpan = TimeSpan.FromSeconds(1.0 / 8) | |
Private Const _animation = "|/-\" | |
Private _animationIndex As Integer = 0 | |
Private _currentProgress As Double = 0.0 | |
Private _currentText As String = String.Empty | |
Public Sub Report(value As Double) Implements IProgress(Of Double).Report | |
' Make sure value is in [0..1] range | |
value = Math.Max(0, Math.Min(1, value)) | |
Interlocked.Exchange(_currentProgress, value) | |
End Sub | |
Public Sub UpdateText(text As String) | |
' Get length of common portion | |
Dim commonPrefixLength = 0 | |
Dim commonLength = Math.Min(_currentText.Length, text.Length) | |
While commonPrefixLength < commonLength _ | |
AndAlso text(commonPrefixLength) = _currentText(commonPrefixLength) | |
commonPrefixLength += 1 | |
End While | |
' Backtrack to the first differing character | |
Dim outputBuilder = New StringBuilder() | |
outputBuilder.Append(ControlChars.Back, _currentText.Length - commonPrefixLength) | |
' Output new suffix | |
outputBuilder.Append(text.Substring(commonPrefixLength)) | |
' If the new text is shorter than the old one: delete overlapping characters | |
Dim overlapCount = _currentText.Length - text.Length | |
If overlapCount > 0 Then | |
outputBuilder.Append(" "c, overlapCount) | |
outputBuilder.Append(ControlChars.Back, overlapCount) | |
End If | |
Console.Write(outputBuilder.ToString()) | |
_currentText = text | |
End Sub | |
Private Const _blockCount = 10 | |
Private _disposed As Boolean = False | |
Private ReadOnly _timer As Timer | |
Public Sub ResetTimer() | |
_timer.Change(_animationInterval, TimeSpan.FromMilliseconds(-1)) | |
End Sub | |
Private Sub TimerHandler(state As Object) | |
SyncLock _timer | |
If _disposed Then Return | |
Dim progressBlockCount = Convert.ToInt32(_currentProgress * _blockCount) | |
Dim percent = Convert.ToInt32(_currentProgress * 100.0) | |
Dim text = String.Format( | |
"[{0}{1}] {2,3}% {3}", | |
New String("#"c, progressBlockCount), | |
New String("-"c, _blockCount - progressBlockCount), | |
percent, | |
_animation(Interlocked.Increment(_animationIndex) Mod _animation.Length) | |
) | |
UpdateText(text) | |
ResetTimer() | |
End SyncLock | |
End Sub | |
Public Sub Dispose() Implements IDisposable.Dispose | |
SyncLock _timer | |
_disposed = True | |
UpdateText(String.Empty) | |
End SyncLock | |
End Sub | |
Public Sub New() | |
_timer = New Timer(AddressOf 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 Not Console.IsOutputRedirected Then | |
ResetTimer() | |
End If | |
End Sub | |
End Class |
Nice, I was looking for this!