System.Windows.Forms.Timer not firing

白昼怎懂夜的黑 提交于 2020-01-01 17:00:34

问题


I want to use a System.Windows.Forms.Timer to ensure that an event fires on the UI thread of an excel addin I'm creating. I construct the timer as follows:

private System.Windows.Forms.Timer _timer;

private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
    Debug.WriteLine("ThisAddIn_Startup:" + Thread.CurrentThread.ManagedThreadId);

    _timer = new System.Windows.Forms.Timer();
    _timer.Tick += new EventHandler(TimerEventHandler);
    _timer.Interval = 500;            
}

The timer is fired by a COM event from a library I am using:

private void OnEvent()
{
    _timer.Start();
}

I then expect the _timer to call the following method when it ticks:

public void TimerEventHandler(object sender, EventArgs args)
{
    _timer.Stop();
    Debug.WriteLine("Tick: " + Thread.CurrentThread.ManagedThreadId);                  
}

As I understand, when I create the timer in the Addin thread, even though it is started from another thread (COM event in this case), it should fire on the thread that it was created on, i.e. the addin thread. However, this doesn't happen.

I have implemented this exact mechanism in an RTDServer I wrote in the past (as outlined by Kenny Kerr) and it works as expected but the _timer in this scenario never ticks.

I have also read other SO articles that point to the same behavior and can't figure out what is different about my addin setup?

EDIT: The OnEvent() method is fired.


回答1:


I initially meant to post this as comment, but it turned to be too long.

Firstly, your thread structure is a bit confusing to me, the way you described it. Put Debug.WriteLine("OnEvent:" + Thread.CurrentThread.ManagedThreadId) inside OnEvent and let us know all thread IDs you see from your debug output.

That said, the rules are:

  • You should create WinForms' Timer object on an STA thread, and the thread should be configured as STA before it starts.

  • This thread may or may not be the main UI thread (where your main form was created), but it still should execute a message loop (with Application.Run) for timer events to fire. There are other ways of pumping messages, but generally you do not control them from .NET code.

  • You should handle the events sourced by WinForms' Timer on the same thread it was created. You can then 'forward' these events to another thread context if you like (using SynchronizationContext Send or Post) but I can't think of any reasons for such complexity.

The answer by @Maarten actually suggests the right way of doing it, in my opinion.




回答2:


The winforms timer is a control and must be used by placing it on a form. You never add it to a control-collection, so I would not expect it to work properly. The documentation says the following

Implements a timer that raises an event at user-defined intervals. This timer is optimized for use in Windows Forms applications and must be used in a window.

Therefore, I would suggest that you use an instance of the System.Timers.Timer class. This class can be used anywhere.

Note that the Tick-event you use above, is called by another name in the System.Timer.Timer class, namely the Elapsed-event.




回答3:


I don't yet understand why the Forms.Timer doesn't operate as expected but the following excellent article explains in detail how to marshal work onto the UI thread: http://www.codeproject.com/Articles/31971/Understanding-SynchronizationContext-Part-I



来源:https://stackoverflow.com/questions/18327567/system-windows-forms-timer-not-firing

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!