Why does System.Timer.Timer still fire events when Enabled is set to false?

Relevant document, System.Timers.Timer.Interval

Note If Enabled and AutoReset are both set to false, and the timer has previously been enabled, setting the Interval property causes the Elapsed event to be raised once, as if the Enabled property had been set to true. To set the interval without raising the event, you can temporarily set the AutoReset property to true.

The recommended solution of setting AutoReset to true does not solve the problem because there is an undocumented behavior of setting AutoReset to true during an event handler also allowing for an event to be fired.

The solution seems to be to build out the derived object to the point where you can keep any of the apparently many ways that an event can be fired again from happening.

Below is the implementation that I ended with.

public class PauseableTimer : Timer
{
    private bool _paused;
    public bool Paused
    {
        get { return _paused; }
        set 
        { 
            Interval = _interval;
            _paused = value;
        }
    }

    new public bool Enabled
    {
        get
        {
            return base.Enabled;
        }
        set
        {
            if (Paused)
            {
                if (!value) base.Enabled = false;
            }
            else
            {
                base.Enabled = value;
            }
        }
    }

    private double _interval;
    new public double Interval
    {
        get { return base.Interval; }
        set
        {
            _interval = value;
            if (Paused){return;}
            if (value>0){base.Interval = _interval;}
        }
    }

    public PauseableTimer():base(1){}

    public PauseableTimer(double interval):base(interval){}
}

Everything is more complex in multithreading, I'm afraid. Assuming your code is working as you wish, there is a window where in-flight events can get raised after you reset the Enabled property. See this quote from the MSDN docs.

The signal to raise the Elapsed event is always queued for execution on a ThreadPool thread. This might result in the Elapsed event being raised after the Enabled property is set to false. The code example for the Stop method shows one way to work around this race condition.


Another option is to suppress the event??? I can't explain what is going but the theory presented below should allow you to circumvent this little problem you have discussed. As Steve mentioned put a 'Watch and break point on the enabled property' that you are try set and make sure it is actually being set.

How would I tackle this:

Catch and check for the 'Enabled' property and remove '-=' the subscribing method (handler) as of when needed and then re-add '+=' it again when you do need handle the 'Elapsed' event.

I have used this style quite a few times on a few different WinForms project. If you don't want the 'Elapsed' event to be handled programmatically create a check for and remove it when a certain condition is met and then add it when the opposite condition is met.

if (paused) // determine pause logic to be true in here
{
   timer.Elapsed -= ... // remove the handling method.
}
else
{
   timer.Elapsed += ... // re-add it in again
}

The above code logic will allow you code to ignore the 'Elapsed' event ever time it is raised whilst the 'Paused' flag is true. I hope the above helps

Tags:

C#

Timer