0

So I am trying to set my countdown timer intervals, so that 1hr = 10minutes (in real time), 1min = 10secs (In real time) and 1s = 0.17s (In realtime) to help when testing my code. I can't seem to find what part of my code to change without causing an error. so I tried defining the interval in the initialise components section and received this:

System.NullReferenceException: 'Object reference not set to an instance of an object.' timer was null.

    private void button1_Click(object sender, EventArgs e)
    {
        AddTimeToClock(TimeSpan.FromSeconds(10));
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        timer = new System.Windows.Forms.Timer();
        timer.Interval = (int)TimeSpan.FromSeconds(1).TotalMilliseconds;
        timer.Tick += OnTimeEvent;
        DisplayTime();
    }
    private void DisplayTime()
    {
        lblTime.Text = countdownClock.ToString(@"hh\:mm\:ss");
    }
    private void OnTimeEvent(object sender, EventArgs e)
    {
        // Subtract whatever our interval is from the countdownClock
        countdownClock = countdownClock.Subtract(TimeSpan.FromMilliseconds(timer.Interval));

        if (countdownClock.TotalMilliseconds <= 0)
        {
            // Countdown clock has run out, so set it to zero 
            // (in case it's negative), and stop our timer
            countdownClock = TimeSpan.Zero;
            timer.Stop();
        }

        // Display the current time
        DisplayTime();
    }
    private void AddTimeToClock(TimeSpan timeToAdd)
    {
        // Add time to our clock
        countdownClock += timeToAdd;

        // Display the new time
        DisplayTime();

        // Start the timer if it's stopped
        //if (!timer.Enabled) timer.Start();
    }

    private void button2_Click(object sender, EventArgs e)
    {
        AddTimeToClock(TimeSpan.FromMinutes(1));
    }

    private void button3_Click(object sender, EventArgs e)
    {
        AddTimeToClock(TimeSpan.FromMinutes(10));
    }

    private void checkBox1_CheckedChanged(object sender, EventArgs e)
    {
        if (checkBox1.Checked) timer.Start();
        else
        {
            timer.Stop();
        }
    }
LarsTech
  • 77,282
  • 14
  • 135
  • 204
RAG
  • 17
  • 6
  • "...without causing an error." - could you perhaps edit your post to include this error and where it occurs? I imagine it would be helpful.. – Broots Waymb May 22 '20 at 13:00
  • so I tried defining the interval in the initialise components section and received this System.NullReferenceException: 'Object reference not set to an instance of an object.' timer was null. – RAG May 22 '20 at 13:04
  • The constructor happens before the Load event. So move all that code to the constructor. – LarsTech May 22 '20 at 13:07
  • Acutally it does. THis ihas onthign to do with setting an interval and everything to do with (a9 a failure to know basic debugging and (b) a failure to identify a standard .net error, the null reference exception. – TomTom May 22 '20 at 13:21

2 Answers2

0

I would suggest you introduce a wrapper for your timer. That way you can expose a interval-method that is scaled however you want. For example:

public class ScaledTimer : IDisposable
{
    private Timer timer;

    public ScaledTimer() => this.timer = new Timer();
    public void Dispose() => timer.Dispose();
    public void Start() => timer.Start();
    public void Stop() => timer.Stop();
    public double Scale { get; set; } = 1;
    public bool Enabled
    {
        get => timer.Enabled;
        set => timer.Enabled = value;
    }

    public int Interval
    {
        get => (int)(timer.Interval * Scale);
        set => timer.Interval = (int)(value / Scale);
    }

    public event EventHandler Tick
    {
        add => timer.Tick += value;
        remove => timer.Tick -= value;
    }
}

Set Scale to 10 to make the timer tick 10 times as fast. Note that this will not work well if you also use DateTime.Now since that will use the actual system time. It might also be a good idea to extract an interface for the timer. This can be useful when unit testing since it allows mocking of the timer. There is also a limited resolution for timers, so it may not work well if the interval is down to ~16ms.

JonasH
  • 6,934
  • 1
  • 3
  • 9
0

If I'm understanding what you are trying to accomplish (ticking at the same speed, but logically running out your countdownClock at 6x speed), then I believe your best plan of action is to add a constant for time scale.

private static readonly int timeScale = 6

Then when you adjust your countdownClock in the OnTimeEvent, you can multiply by the scale.

countdownClock = countdownClock.Subtract(timeScale * TimeSpan.FromMilliseconds(timer.Interval));

Set your timeScale to 1 to run at normal speed, or to higher values to run faster.

This would give a general idea of how to accomplish your goal, but require hand-editing of the timeScale value when debugging to get your desired behavior. From here, you can decide how to best manage this - maybe you want to load your timeScale from a config file, or adjust it based on whether #DEBUG is defined. You may also want to make a countdownClock class that has a Scale property to further encapsulate this behavior. Final implementation is up to you.

  • Sorry, not a very experienced coder but where would I place the private static line. I'm not too sure what you mean by debugging this either. So in addition to the code you provided what else would I need to consider adding as using those two lines alone does not seem to work. Apologies, if this is a stupid question unfortunately my uni course set this coursework with our only lecturer now off absent. – RAG May 22 '20 at 13:39