' ***********************************************************************
' Author : Elektro
' Modified : 12-December-2015
' ***********************************************************************
#Region " Public Members Summary "
#Region " Constructors "
' New()
#End Region
#Region " Events "
' TimeUpdated(Object, TimeMeasurerUpdatedEventArgs)
#End Region
#Region " Properties "
' MaxValue As Double
' State As TimeMeasurerState
' UpdateInterval As Integer
#End Region
#Region " Methods "
' Start(Double)
' Start(Date, Date)
' Start(TimeSpan)
' Pause()
' Resume()
' Stop()
' Dispose()
#End Region
#End Region
#Region " Usage Examples "
#End Region
#Region " Option Statements "
Option Strict On
Option Explicit On
Option Infer Off
#End Region
#Region " Imports "
Imports System.ComponentModel
Imports System.Windows.Forms
' Imports Elektro.Core.DateAndTime.Enums
' Imports Elektro.Core.DateAndTime.Types.EventArgs
' Imports Elektro.Core.Types
#End Region
#Region " Time Measurer "
Namespace DateAndTime.Types
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' Measures the elapsed and/or remaining time of a time interval.
''' The time measurer can be used as a chronometer or a countdown.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
<ImmutableObject(False)>
Public NotInheritable Class TimeMeasurer : Implements IDisposable ': Inherits AestheticObject
#Region " Private Fields "
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' An <see cref="Stopwatch"/> instance to retrieve the elapsed time. (a chronometer)
''' </summary>
''' ----------------------------------------------------------------------------------------------------
Private timeElapsed As Stopwatch
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' A <see cref="TimeSpan"/> instance to retrieve the remaining time. (a countdown)
''' </summary>
''' ----------------------------------------------------------------------------------------------------
Private timeRemaining As TimeSpan
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' A <see cref="Timer"/> instance that updates the elapsed and remaining time,
''' and also raise <see cref="TimeMeasurer"/> events.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
Private WithEvents measureTimer As Timer
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' Flag that indicates wheter this <see cref="TimeMeasurer"/> instance has finished to measure time interval.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
Private isFinished As Boolean
#End Region
#Region " Properties "
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' Gets the maximum time that the <see cref="TimeMeasurer"/> can measure, in milliseconds.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
''' <value>
''' The maximum time that the <see cref="TimeMeasurer"/> can measure, in milliseconds.
''' </value>
''' ----------------------------------------------------------------------------------------------------
Public Shared ReadOnly Property MaxValue As Double
<DebuggerStepThrough>
Get
Return (TimeSpan.MaxValue.TotalMilliseconds - 1001.0R)
End Get
End Property
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' Gets the current state of this <see cref="TimeMeasurer"/> instance.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
''' <value>
''' The current state of this <see cref="TimeMeasurer"/> instance.
''' </value>
''' ----------------------------------------------------------------------------------------------------
Public ReadOnly Property State As TimeMeasurerState
<DebuggerStepThrough>
Get
If (Me.timeElapsed Is Nothing) OrElse (Me.isFinished) Then
Return TimeMeasurerState.Disabled
ElseIf Not Me.timeElapsed.IsRunning Then
Return TimeMeasurerState.Paused
Else
Return TimeMeasurerState.Enabled
End If
End Get
End Property
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' Gets or sets the update interval.
''' Maximum value is 1000 (1 second).
''' </summary>
''' ----------------------------------------------------------------------------------------------------
''' <value>
''' The update interval.
''' </value>
''' ----------------------------------------------------------------------------------------------------
''' <exception cref="ArgumentException">
''' A value smaller than 1000 is required.;value
''' </exception>
Public Property UpdateInterval As Integer
<DebuggerStepThrough>
Get
Return Me.updateIntervalB
End Get
<DebuggerStepThrough>
Set(ByVal value As Integer)
If (value > 1000) Then
Throw New ArgumentException(message:="A value smaller than 1000 is required.", paramName:="value")
Else
Me.updateIntervalB = value
If (Me.measureTimer IsNot Nothing) Then
Me.measureTimer.Interval = value
End If
End If
End Set
End Property
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' ( Backing field )
''' The update interval.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
Private updateIntervalB As Integer = 100I
#End Region
#Region " Events "
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' Occurs when the elapsed/remaining time updates.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
Public Event TimeUpdated(ByVal sender As Object, ByVal e As TimeMeasurerUpdatedEventArgs)
#End Region
#Region " Constructors "
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' Initializes a new instance of the <see cref="TimeMeasurer"/> class.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
<DebuggerStepThrough>
Public Sub New()
End Sub
#End Region
#Region " Public Methods "
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' Starts the time interval measurement.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
''' <param name="milliseconds">
''' The time interval to measure, in milliseconds.
''' </param>
''' ----------------------------------------------------------------------------------------------------
''' <exception cref="ArgumentOutOfRangeException">
''' milliseconds;A value smaller than <see cref="TimeMeasurer.MaxValue"/> is required.
''' </exception>
'''
''' <exception cref="ArgumentOutOfRangeException">
''' milliseconds;A value greater than 0 is required.
''' </exception>
''' ----------------------------------------------------------------------------------------------------
<DebuggerStepThrough>
Public Sub Start(ByVal milliseconds As Double)
If (milliseconds > TimeMeasurer.MaxValue) Then
Throw New ArgumentOutOfRangeException(paramName:="milliseconds",
message:=String.Format("A value smaller than {0} is required.", TimeMeasurer.MaxValue))
ElseIf (milliseconds <= 0) Then
Throw New ArgumentOutOfRangeException(paramName:="milliseconds",
message:="A value greater than 0 is required.")
Else
Me.timeElapsed = New Stopwatch
Me.timeRemaining = TimeSpan.FromMilliseconds(milliseconds)
Me.measureTimer = New Timer With
{
.Tag = milliseconds,
.Interval = Me.updateIntervalB,
.Enabled = True
}
Me.isFinished = False
Me.timeElapsed.Start()
Me.measureTimer.Start()
End If
End Sub
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' Starts a time interval measurement given a difference between two dates.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
''' <param name="startDate">
''' The starting date.
''' </param>
'''
''' <param name="endDate">
''' The ending date.
''' </param>
''' ----------------------------------------------------------------------------------------------------
<DebuggerStepThrough>
Public Sub Start(ByVal startDate As Date,
ByVal endDate As Date)
Me.Start(endDate.Subtract(startDate).TotalMilliseconds)
End Sub
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' Starts a time interval measurement given a <see cref="TimeSpan"/>.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
''' <param name="time">
''' A <see cref="TimeSpan"/> instance that contains the time interval.
''' </param>
''' ----------------------------------------------------------------------------------------------------
<DebuggerStepThrough>
Public Sub Start(ByVal time As TimeSpan)
Me.Start(time.TotalMilliseconds)
End Sub
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' Pauses the time interval measurement.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
''' <exception cref="Exception">
''' TimeMeasurer is not running.
''' </exception>
''' ----------------------------------------------------------------------------------------------------
<DebuggerStepThrough>
Public Sub Pause()
If (Me.State <> TimeMeasurerState.Enabled) Then
Throw New Exception("TimeMeasurer is not running.")
Else
Me.measureTimer.Stop()
Me.timeElapsed.Stop()
End If
End Sub
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' Resumes the time interval measurement.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
''' <exception cref="Exception">
''' TimeMeasurer is not paused.
''' </exception>
''' ----------------------------------------------------------------------------------------------------
<DebuggerStepThrough>
Public Sub [Resume]()
If (Me.State <> TimeMeasurerState.Paused) Then
Throw New Exception("TimeMeasurer is not paused.")
Else
Me.measureTimer.Start()
Me.timeElapsed.Start()
End If
End Sub
''' <summary>
''' Stops the time interval measurement.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
''' <exception cref="Exception">
''' TimeMeasurer is not running.
''' </exception>
''' ----------------------------------------------------------------------------------------------------
<DebuggerStepThrough>
Public Sub [Stop]()
If (Me.State = TimeMeasurerState.Disabled) Then
Throw New Exception("TimeMeasurer is not running.")
Else
Me.Reset()
Me.isFinished = True
Me.measureTimer.Stop()
Me.timeElapsed.Stop()
End If
End Sub
#End Region
#Region " Private Methods "
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' Stops Time intervals and resets the elapsed and remaining time to zero.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
<DebuggerStepThrough>
Private Sub Reset()
Me.measureTimer.Stop()
Me.timeElapsed.Reset()
End Sub
#End Region
#Region " Event Handlers "
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' Handles the <see cref="Timer.Tick"/> event of the <see cref="MeasureTimer"/> timer.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
''' <param name="sender">
''' The source of the event.
''' </param>
'''
''' <param name="e">
''' The <see cref="EventArgs"/> instance containing the event data.
''' </param>
''' ----------------------------------------------------------------------------------------------------
<DebuggerStepThrough>
Private Sub MeasureTimer_Tick(ByVal sender As Object, ByVal e As Global.System.EventArgs) _
Handles measureTimer.Tick
Dim timeDiff As TimeSpan = (Me.timeRemaining - Me.timeElapsed.Elapsed)
' If finished...
If (timeDiff.TotalMilliseconds <= 0.0R) _
OrElse (Me.timeElapsed.ElapsedMilliseconds > DirectCast(Me.measureTimer.Tag, Double)) Then
Me.Reset()
Me.isFinished = True
If (Me.TimeUpdatedEvent IsNot Nothing) Then
Dim goal As TimeSpan = TimeSpan.FromMilliseconds(DirectCast(Me.measureTimer.Tag, Double))
RaiseEvent TimeUpdated(sender, New TimeMeasurerUpdatedEventArgs(goal, TimeSpan.FromMilliseconds(0.0R), goal))
End If
Else ' If not finished...
If (Me.TimeUpdatedEvent IsNot Nothing) Then
RaiseEvent TimeUpdated(sender, New TimeMeasurerUpdatedEventArgs(Me.timeElapsed.Elapsed,
timeDiff,
TimeSpan.FromMilliseconds(DirectCast(Me.measureTimer.Tag, Double))))
End If
End If
End Sub
#End Region
#Region " IDisposable Implementation "
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' Flag to detect redundant calls when disposing.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
Private isDisposed As Boolean = False
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' Releases all the resources used by this instance.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
<DebuggerStepThrough>
Public Sub Dispose() Implements IDisposable.Dispose
Me.Dispose(isDisposing:=True)
GC.SuppressFinalize(obj:=Me)
End Sub
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
''' Releases unmanaged and, optionally, managed resources.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
''' <param name="isDisposing">
''' <see langword="True"/> to release both managed and unmanaged resources;
''' <see langword="False"/> to release only unmanaged resources.
''' </param>
''' ----------------------------------------------------------------------------------------------------
<DebuggerStepThrough>
Private Sub Dispose(ByVal isDisposing As Boolean)
If (Not Me.isDisposed) AndAlso (isDisposing) Then
If (Me.measureTimer IsNot Nothing) Then
Me.measureTimer.Stop()
Me.measureTimer.Dispose()
Me.measureTimer = Nothing
End If
If (Me.TimeUpdatedEvent IsNot Nothing) Then
RemoveHandler Me.TimeUpdated, Me.TimeUpdatedEvent
End If
End If
Me.isDisposed = True
End Sub
#End Region
End Class
End Namespace
#End Region