从计时器线程调用GUI线程上的方法

10
在我的应用程序中,我使用定时器来检查RSS订阅中的更新,如果发现新项目,我会弹出一个自定义对话框来通知用户。当我手动运行检查时,一切正常,但是当自动检查在计时器Elapsed事件中运行时,自定义对话框没有显示。
首先,这是一个线程问题吗?(我假设它是因为手动和自动检查都使用相同的代码)。
当我运行自动检查时,我是否需要从Timers Elapsed事件处理程序调用运行检查的方法?
我的自定义对话框类中是否有什么需要做的事情?
编辑:这是一个WinForms应用程序。
以下是代码示例(请不要指出此代码示例中的语法错误,这只是一个简单的示例,而不是真实的代码)。
public class MainForm : System.Windows.Forms.Form
{
    //This is the object that does most of the work.
    ObjectThatDoesWork MyObjectThatDoesWork = new ObjectThatDoesWork(); 
    MyObjectThatDoesWork.NewItemsFound += new NewItemsFoundEventHandler(Found_New_Items);

    private void Found_New_Items(object sender, System.EventArgs e)
    {
        //Display custom dialog to alert user.
    }

    //Method that doesn't really exist in my class, 
    // but shows that the main form can call Update for a manual check.
    private void Button_Click(object sender, System.EventArgs e)
    {
        MyObjectThatDoesWork.Update();
    }

    //The rest of MainForm with boring main form stuff
}


public class ObjectThatDoesWork
{
    System.Timers.Timer timer;

    public ObjectThatDoesWork()
    {
        timer = new System.Timers.Timer();
        timer.Interval = 600000;
        timer.AutoReset = true;
        timer.Elapsed += new new System.Timers.ElapsedEventHandler(TimeToWork);
        timer.Start();
    }

    private void TimeToWork(object sender, System.Timers.ElapsedEventArgs e)
    {
        Update();
    }

    public void Update()
    {
        //Check for updates and raise an event if new items are found.
        //The event is consumed by the main form.
        OnNewItemsFound(this);
    }

    public delgate void NewItemsFoundEventHandler(object sender, System.EventArgs e);
    public event NewItemsFoundEventHandler NewItemsFound;
    protected void OnNewItemsFound(object sender)
    {
        if(NewItemsFound != null)
        {
            NewItemsFound(sender, new System.EventArgs());
        }
    }
}

阅读了一些评论和答案后,我认为我的问题在于我使用的是 System.Timers.Timer 而不是 System.Windows.Forms.Timer
编辑:
更改为 Forms.Timer 后测试结果良好(但尚未存在新项目,因此尚未看到自定义对话框)。我添加了一些代码,在调用更新方法时将线程 ID 输出到文件中。使用 Timers.Timer 时,线程 ID 不是 GUI 线程,但使用 Forms.Timer 时,线程 ID 与 GUI 相同。

3
没错,这是一个线程问题,但答案取决于你正在使用 WPF 还是 WinForms:你在用哪个? - x0n
你没有看到任何异常,这让我感到惊讶:你在哪个版本的.NET上运行它?能否展示一些样例代码? - Eamon Nerbonne
@Tester101 - 如果你从工具箱中拖放 System.Timers.Timer 到窗体上,它将自动在 GUI 线程上触发事件。请参阅我在答案中提供的链接文章。 - Giorgi
@Giorgi:计时器不在窗体上,这就是我最初使用 System.Timers.Timer 的原因。 - Tester101
@smirkingman:NewItemsFound 是一个老套的事件定义,所以我认为它对这个示例并不是很重要。 - Tester101
显示剩余4条评论
4个回答

20

更改为System.Windows.Forms.Timer解决了问题。谢谢。 - Tester101
+1000!System.Windows.Forms.Timer解决了我非常令人沮丧的“在另一个线程上创建对象”的计时器问题!谢谢! - Michael Rodrigues
就像一个笑话一样,我花了一整天的时间!谢谢并注意。 - aozan88

1
你应该在这里使用 Forms.Timer,或者如果你使用其他类型的计时器,请使用 .Invoke() 序列化对 UI 的调用。

0
    private static System.Threading.SynchronizationContext _UI_Context;
    //call this function once from the UI thread
    internal static void init_CallOnUIThread()
    {
        _UI_Context = System.Threading.SynchronizationContext.Current;
    }
    public static void CallOnUIThread(Action action, bool asynchronous = false)
    {
        if (!asynchronous)
            _UI_Context.Send((o) =>
            {
                action();
            }, null);
        else
            _UI_Context.Post((o) =>
            {
                action();
            }, null);
    }

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接