I've written a custom Start-SleepNoUIHang
function to sleep a windows form UI whilst not hanging it to allow for user interaction using a ForEach
loop and inside that loop it calls [System.Windows.Forms.Application]::DoEvents()
to prevent it from doing so.
It works as I intended but the only trouble is that the function slightly drifts past the argument $Milliseconds
.
If I set that to say 5000
the timer takes around 6300
milliseconds.
I've tried to add a counter inside the ForEach
loop and then break out of it once it reaches the $Milliseconds
argument but that doesn't seem to work.
I didn't want to use the .net timer so I created this as a one-liner to use anywhere in the program where it was needed.
Any help here would be greatly appreciated.
Here's the code (with comments):
<#
This function attempts to pause the UI without hanging it without the need for a
timer event that does work.
The only trouble is that the timer slight drifts more than the provided
$Milliseconds argument.
#>
function Start-SleepNoUIHang {
Param
(
[Parameter(Mandatory = $false, HelpMessage = 'The time to wait in milliseconds.')]
[int]$Milliseconds
)
$timeBetween = 50 # This value seems to be a good value in order for the UI not to hang itself.
$timeElapsed = 0 # Increment this to check and break out of the ForEach loop.
# ($Milliseconds/$timeBetween)*$timeBetween # Time is the total wait time in milliseconds.
1..($Milliseconds/$timeBetween) | ForEach {
Start-Sleep -Milliseconds $timeBetween
Try { [System.Windows.Forms.Application]::DoEvents() } catch{} # A try catch here in case there's no windows form.
$timeElapsed = $timeElapsed + $timeBetween # Increment the $timeElapsed counter.
Write-Host $timeElapsed
# This doesn't seem to have any effect on the timer. It ends on its own accord.
if ($timeElapsed -gt $Milliseconds) {
Write-Host 'Break'
break
}
}
}
$elapsed = [System.Diagnostics.Stopwatch]::StartNew()
Write-Host "Started at $(get-date)"
Start-SleepNoUIHang -Milliseconds 5000
Write-Host "Ended at $(get-date)"
Write-Host "Total Elapsed Time: $($elapsed.Elapsed.ToString())"
I've also tried to do a While loop replacing the ForEach loop with this but that behaved the same.
While ( $Milliseconds -gt $timeElapsed ) {
$timeElapsed = $timeElapsed + $timeBetween # Increment the $timeElapsed counter.
Start-Sleep -Milliseconds $timeBetween
Write-Host $timeElapsed
}