Fredou
Fredou

Reputation: 20110

Proper way to pause a System.Timers.Timer?

I'm looking at how to pause a System.Timers.Timer and I cannot figure out the proper way to pause it without resetting the timer.

How to pause it?

Upvotes: 7

Views: 14221

Answers (6)

Christophe
Christophe

Reputation: 699

Based on previous comments from sveilleux2 and Tomerz, I improved the solution to implement IDisposable, allow multiple pause-resume calls, plus add Enabled and Paused properties.

public class PausableTimer: IDisposable
{
    private Timer _timer;
    private Stopwatch _stopWatch;
    private bool _paused;
    private double _remainingTimeBeforePause;
    public event ElapsedEventHandler Elapsed;

    public PausableTimer()
    {
        _stopWatch = new Stopwatch();

        _timer = new Timer();
        _timer.AutoReset = false;
        _timer.Elapsed += (sender, arguments) =>
        {
            Elapsed?.Invoke(sender, arguments);

            if (_timer != null && _timer.AutoReset)
            {
                _stopWatch.Restart();
            }
        };

    }

    public PausableTimer(double interval): this()
    {
        Interval = interval;
    }

    public bool AutoReset
    {
        get
        {
            return _timer.AutoReset;
        }
        set
        {
            _timer.AutoReset = value;
        }
    }

    public bool Enabled
    {
        get
        {
            return _timer.Enabled;
        }
        set
        {
            _timer.Enabled = value;
        }
    }

    public double Interval
    {
        get
        {
            return _timer.Interval;
        }
        set
        {
            _timer.Interval = value;
        }
    }

    public bool Paused
    {
        get
        {
            return _paused;
        }
    }

    public void Start()
    {
        _timer.Start();
        _stopWatch.Restart();
    }

    public void Stop()
    {
        _timer.Stop();
        _stopWatch.Stop();
    }

    public void Pause()
    {
        if (!_paused && _timer.Enabled)
        {
            _paused = true;
            _stopWatch.Stop();
            _timer.Stop();
            _remainingTimeBeforePause = Math.Max(0, Interval - _stopWatch.ElapsedMilliseconds);
        }
    }

    public void Resume()
    {
        if (_paused)
        {
            _paused = false;
            if (_remainingTimeBeforePause > 0)
            {
                _timer.Interval = _remainingTimeBeforePause;
                _timer.Start();
                _stopWatch.Start();
            }
        }
    }

    bool _disposed = false;

    public void Dispose()
    {
        if (_timer != null && !_disposed)
        {
            // Not thread safe...
            _disposed = true;
            _timer.Dispose();
            _timer = null;
        }
    }

    ~PausableTimer()
    {
        Dispose();
    }
}

Upvotes: 0

sveilleux2
sveilleux2

Reputation: 1512

Here is what I've been using. It's maybe not state-of-the-art accurate but works pretty good. At least, it's a good starting point for someone trying to pause/resume at timer.

public class PausableTimer
{
    private Timer _timer;
    private Stopwatch _stopWatch;
    private bool _paused;
    private double _interval;
    private double _remainingTimeBeforePause;

    public PausableTimer(double interval, ElapsedEventHandler handler)
    {
        _interval = interval;
        _stopWatch = new Stopwatch();

        _timer = new Timer(interval);
        _timer.Elapsed += (sender, arguments) => {
            if (handler != null)
            {
                if(_timer.AutoReset)
                {
                    _stopWatch.Restart();
                }

                handler(sender, arguments);
            }
        };

        _timer.AutoReset = false;
    }

    public bool AutoReset
    {
        get
        {
            return _timer.AutoReset;
        }
        set
        {
            _timer.AutoReset = value;
        }
    }

    public void Start()
    {
        _timer.Start();
        _stopWatch.Restart();
    }

    public void Stop()
    {
        _timer.Stop();
        _stopWatch.Stop();
    }

    public void Pause()
    {
        if(!_paused && _timer.Enabled)
        {
            _stopWatch.Stop();
            _timer.Stop();
            _remainingTimeBeforePause = Math.Max(0, _interval - _stopWatch.ElapsedMilliseconds);
            _paused = true;
        }
    }

    public void Resume()
    {
        if(_paused)
        {
            _paused = false;
            if(_remainingTimeBeforePause > 0)
            {
                _timer.Interval = _remainingTimeBeforePause;
                _timer.Start();
            }
        }
    }
}

Upvotes: 0

wenhuix
wenhuix

Reputation: 51

Create a timer like this:

    System.Timers.Timer t = new System.Timers.Timer(1000) 
    t.Elapsed += new System.Timers.ElapsedEventHandler(timerEvent);

    public void timerEvent(object source, System.Timers.ElapsedEventArgs e)
    {

    }

you can set this property to start or pause the timer execute the timeEvent:

Start:

    t.Enabled = true 

Pause:

    t.Enabled = false

Upvotes: -1

Fredou
Fredou

Reputation: 20110

I got that for now, I'm sure it's not bulletproof so tell me what is wrong with it...

Public Class ImprovedTimer
Inherits System.Timers.Timer

Private _sw As System.Diagnostics.Stopwatch
Private _paused As Boolean
Private _originalInterval As Double
Private _intervalRemaining As Double?

Public ReadOnly Property IntervalRemaining() As Double?
    Get
        Return _intervalRemaining
    End Get
End Property

Public ReadOnly Property Paused() As Boolean
    Get
        Return _paused
    End Get
End Property

Public ReadOnly Property OriginalInterval() As Double
    Get
        Return _originalInterval
    End Get
End Property

Public Sub Pause()
    If Me.Enabled Then
        _intervalRemaining = Me.Interval - _sw.ElapsedMilliseconds
        _paused = True
        resetStopWatch(False, False)
        MyBase.Stop()
    End If
End Sub

Public Sub [Resume]()
    If _paused Then
        Me.Interval = If(_intervalRemaining.HasValue, _intervalRemaining.Value, _originalInterval)
        resetStopWatch(True, False)
        MyBase.Start()
    End If
End Sub

Public Overloads Property Enabled() As Boolean
    Get
        Return MyBase.Enabled
    End Get
    Set(ByVal value As Boolean)
        MyBase.Enabled = value
        resetStopWatch(MyBase.Enabled, True)
    End Set
End Property

Public Overloads Sub Start()
    resetStopWatch(True, True)
    MyBase.Start()
End Sub

Public Overloads Sub [Stop]()
    resetStopWatch(False, True)
    MyBase.Stop()
End Sub

Public Overloads Property Interval() As Double
    Get
        Return MyBase.Interval
    End Get
    Set(ByVal value As Double)
        MyBase.Interval = value
        If Not _paused Then
            _originalInterval = MyBase.Interval
        End If
    End Set
End Property

Private Sub resetStopWatch(ByVal startNew As Boolean, ByVal resetPause As Boolean)
    If _sw IsNot Nothing Then
        _sw.Stop()
        _sw = Nothing
    End If
    If resetPause Then
        If _paused Then
            Me.Interval = _originalInterval
        End If
        _paused = False
        _intervalRemaining = Nothing
    End If
    If startNew Then
        _sw = System.Diagnostics.Stopwatch.StartNew
    End If
End Sub

Private Sub ImprovedTimer_Disposed(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Disposed
    resetStopWatch(False, True)
End Sub

Private Sub ImprovedTimer_Elapsed(ByVal sender As Object, ByVal e As System.Timers.ElapsedEventArgs) Handles Me.Elapsed
    resetStopWatch(Me.AutoReset, True)
End Sub

End Class

Upvotes: 4

Kasper Holdum
Kasper Holdum

Reputation: 13363

You should follow the advice Shay Erlichmen gave. You'll need to save the time remaining when pausing, and continue from that point when the timer is resumed. As for what is wrong with your current code:

Me.Interval = Me.Interval - sw.ElapsedMilliseconds

The above code will make sure that next time you resume it will work as intended on the first tick, but on the continouos ticks you'll have Me.Interval - sw.ElapsedMilliseconds as the interval instead of the original set interval.

Upvotes: 0

Shay Erlichmen
Shay Erlichmen

Reputation: 31928

There is no Pause(), you can write one that on Pause():

  1. Change(Timeout.Infinite, Timeout.Infinite)
  2. Save the calculate the amount of timer remaining.

on Resume():

  1. Change(amount of timer remaining)

If you write this class please post it in answer as it seem that many of us need that functionality out of the Timer class. :)

Upvotes: 9

Related Questions