2

I get the error "Cross-thread operation not valid: Control 'label1' accessed from a thread other than the thread it was created on." when I run this code:

using System;
using System.ComponentModel;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.Timers;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        System.Timers.Timer T = new System.Timers.Timer();
        public Form1()
        {
            InitializeComponent();
            T.Elapsed += new ElapsedEventHandler(T_Elapsed);
            T.Start();
        }

        void T_Elapsed(object sender, ElapsedEventArgs e)
        {
            label1.Text = "This will not work";
        }
    }
}

I thought events ran in the same thread as they were triggered in.

H H
  • 263,252
  • 30
  • 330
  • 514
George Powell
  • 9,141
  • 8
  • 35
  • 43

8 Answers8

3

We have 3 Timer classes in NET (Timers.Timer, Threading.Timer and Windows.Forms.Timer) but only the Windows.Forms one has a Tick event.

When used normally (ie dragged to the Form in Design-Time or created in some Form Code) the event is running on the Main thread and your problem should not occur.

It is therefore most likely that you create the Timer object in another thread, you should probably Edit your question to show us how/where you create it and tell us if it is on another Thread on purpose.

H H
  • 263,252
  • 30
  • 330
  • 514
3

While the "Accepted" answer is technically correct (in that this will fix the problem) this doesn't answer the question.

The ANSWER is to use

void T_Elapsed(object sender, ElapsedEventArgs e)
{
    this.BeginInvoke(new MethodInvoker(delegate(){
        label1.Text = "This will work";
    }));
}

http://jaysonknight.com/blog/archive/2007/02/14/using-anonymous-methods-for-control-invoke-control-begininvoke.aspx

Blue Toque
  • 1,775
  • 13
  • 24
  • What if I want to pass in arguments? – john k Feb 23 '14 at 05:03
  • * Access properties on the sender by casting to the correct type * Access properties in the timer's containing type Be careful not to cause a race condition as you are accessing from a different thread. Google "thread safe" – Blue Toque Feb 23 '14 at 05:28
2

You might be using the wrong kind of timer. Try the WinForms timer, its runs on the GUI thread so you don't have to do Invoke

MetaMapper
  • 968
  • 8
  • 22
1

Are you remembering to use InvokeRequired? It will allow you to update a UI element on the UI thread from the Timer thread.

David
  • 72,686
  • 18
  • 132
  • 173
1

I'm assuming you're talking about a WinForms application.

When trying to update a Form element (which lives on the UI thread) from another thread, you need to use Control.Invoke or Control.BeginInvoke. You pass a delegate to the method you want to invoke (or pass an anonymous method in) and then that delegate gets called on the UI thread rather than the calling thread.

Justin Niessner
  • 242,243
  • 40
  • 408
  • 536
1

Yes, events are executed in the same thread which triggered them. It just so happens that System.Timers.Timer uses a ThreadPool thread by default when raising the Elapsed event. Use the SynchronizingObject property to cause the Elapsed event handler to execute on the thread hosting the target object instead.

public partial class Form1 : Form
{
  System.Timers.Timer T = new System.Timers.Timer();

  public Form1()
  {
    InitializeComponent();
    T.Elapsed += new ElapsedEventHandler(T_Elapsed);
    T.SynchronizingObject = this;
    T.Start();
  }

  void T_Elapsed(object sender, ElapsedEventArgs e)
  {
    label1.Text = "This will not work";
  }
}
Brian Gideon
  • 47,849
  • 13
  • 107
  • 150
0

If you're doing this asynchronously (it sounds like you are), be sure that you catch exceptions in the event handler or callback. If a background thread throws an exception, it will crash the app. This is the most common cause that I've seen of this behavior.

Jeff Tucker
  • 3,387
  • 22
  • 19
-1

I am not a winform developer. But i heard something regararding Timer class which you are using. I think you might want to set these properties and check.

T.Interval = 5000; //in Mili Seconds
T.Enabled = true;
Nirlep
  • 566
  • 1
  • 5
  • 13
  • Neither of them are needed, the Timers constructor sets the Interval, and the T.Start() is exactly the same as T.Enabled = true. I was using the wrong type of Timer, I should have used the Windows.Forms.Timer (Which you can create from the start by dragging it from the Toolbox in design mode). – George Powell Aug 28 '09 at 22:27