1

I recently started learning C#, I am trying to repeat a method every minute with help of a timer. The method changes the value of the label. However, I get the following error:

$exception {"Cross-thread operation not valid: Control 'label1' accessed from a thread other than the thread it was created on."} System.Exception {System.InvalidOperationException}

I have tried searching for a solution and each thread confuses me. I don't just need the correct code but also explanation as I want to learn manipulating UI with help of Timers and Threading.

The following is my code:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Timers;
using System.Windows.Forms;



namespace Winforms_Timer_and_Thread
{
    public partial class Form1 : Form
    {

        System.Timers.Timer myTimer = new System.Timers.Timer();
        public Form1()
        {
            myTimer.Enabled = true;
            InitializeComponent();
        }

        public void startMethod(object senter, ElapsedEventArgs e)
        {
            int cntr = 0;
            cntr++;
            label1.Text = "Executed: " + cntr.ToString();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            label1.Text = "Started!";
            myTimer.Enabled = true;
            myTimer.Interval=(1*60*1000);
            myTimer.Elapsed += new System.Timers.ElapsedEventHandler(startMethod);

        }

    }
}
Tango
  • 386
  • 1
  • 6
  • 29
  • See https://stackoverflow.com/questions/661561/how-to-update-the-gui-from-another-thread-in-c You can't access UI directly from non-ui thread. – sasha_gud May 26 '17 at 19:40
  • 2
    You might also consider using a System.Windows.Forms.Timer instead. – James R. May 26 '17 at 19:42
  • @James R. But then is it possible to call the method every minute? – Tango May 26 '17 at 19:44
  • @Tango - Yes, but it's a very different kind of mechanism. It doesn't really involve multiple threads. Under the covers, Windows will send a WM_TIMER message to the HWND of your form on the interval given. At least that's how I suspect it's implemented. – James R. May 26 '17 at 19:48
  • Please see https://stackoverflow.com/a/4532859/11683 – GSerg May 26 '17 at 19:50

1 Answers1

3

If you change to use a System.Windows.Forms.Timer, you won't have that problem, since it's executed in the same UI thread.

You only have to change:

  • The type of the timer from System.Timers.Timer to System.Windows.Forms.Timer
  • The event that gets subscribed to from Elapsed to Tick
  • The event signature, where ElapsedEventArgs e becomes EventArgs e

You also only need to subscribe to the event once, not each time the timer is enabled, so move that to the Form_Load event instead, along with the Interval assignment (although that can be changed any time).

You also might want to store the counter variable outside of the StartMethod, so it increments each time it's executed, and then reset it to zero each time you start the timer:

public partial class Form1 : Form
{
    readonly System.Windows.Forms.Timer myTimer = new System.Windows.Forms.Timer();
    private int tickCounter;

    public Form1()
    {
        InitializeComponent();

        myTimer.Interval = (int)TimeSpan.FromMinutes(1).TotalMilliseconds;
        myTimer.Tick += StartMethod;
    }

    private void StartMethod(object sender, EventArgs e)
    {
        tickCounter++;
        label1.Text = "Number of executions: " + tickCounter;
    }

    private void button1_Click(object sender, EventArgs e)
    {
        tickCounter = 0;
        label1.Text = "Started!";
        myTimer.Enabled = true;
    }
}
Rufus L
  • 36,127
  • 5
  • 30
  • 43