在后台使用定时器更新UI?

9
我有一个小问题需要解决。 我希望每10秒更新一次UI界面上的内容。我最初使用了DispatcherTimer,但是因为更新方法需要从网络加载一些东西并且这个操作需要一些时间,所以这样会阻塞我的UI界面。 现在我考虑使用一种后台线程来更新。我找到了BackgroundTasks。 然而,据我所知,BackgroundTasks应该作为更新程序,即使应用程序被挂起也能够执行。但我不需要那样做,我只想在应用程序运行时进行更新,而不是在应用程序挂起时更新。 有没有好的方法解决这个问题? 您有什么建议可以用来解决这个问题吗? 提前感谢您!

首先,你尝试了什么?你可以启动一个线程并与其交互。参见以下链接:1. http://msdn.microsoft.com/en-us/library/aa645740%28v=vs.71%29.aspx 2. http://msdn.microsoft.com/en-us/library/ms173178%28v=vs.80%29.aspx 3. https://www.google.ca/#hl=en&sugexp=les%3B&gs_nf=3&tok=BSvnKvGpLPeMsccfqVbvVQ&cp=9&gs_id=bz&xhr=t&q=thread+c%23&pf=p&tbo=d&sclient=psy-ab&oq=thread+c%23&gs_l=&pbx=1&bav=on.2,or.r_gc.r_pw.r_cp.r_qf.&fp=d74e08ea70650bbd&bpcl=38897761&biw=1280&bih=892 4. http://msdn.microsoft.com/en-us/library/system.threading.thread.aspx 5. http://msdn.microsoft.com/en-us/library/ms173178。 - jordan
我认为使用后台工作线程没有任何问题。它不应该引起任何问题,但如果您不想使用后台工作线程,可以使用线程,这基本上是后台工作线程带有一些附加功能。请查看我的答案以获取有关如何执行此操作的信息。 - FrostyFire
4个回答

12

你需要两件事情:

  1. 计时器

    你可以使用间隔为10秒的 System.Timers.Timer 更新UI。

  2. 调度程序

    你需要使用 Dispatcher.Invoke 来更改UI,而无需持有主UI线程。相反,方法 Process 应该在一个与 主UI线程 不同的线程(Timer 方法)上被调用,并在其中使用 Dispatcher 来提醒主UI线程进行更改。

  3. Process() // method to be called after regular interval in Timer
    {
        // lengthy process, i.e. data fetching and processing etc.
    
        // here comes the UI update part
        Dispatcher.Invoke((Action)delegate() { /* update UI */ });
    }
    

System.Timers.Timer不能用于更新UI,你试过了吗?这是MSDN上写的,但我还没有尝试过:如果你在用户界面元素(如窗体或控件)中使用System.Timers.Timer类,而不将计时器放置在该用户界面元素上,请将包含Timer的窗体或控件分配给SynchronizingObject属性,以便事件被调度到用户界面线程。https://msdn.microsoft.com/zh-cn/library/system.timers.timer(v=vs.110).aspx - Mohanad Haddadin
1
我尝试了一下,它可以工作。你需要添加以下代码行来将表单分配为同步对象:t.SynchronizingObject = (this); - Mohanad Haddadin

1
你需要创建一个线程来运行从网站获取和处理信息的代码部分。这样,你的表单就不会因为处理部分与其在不同的线程上而犹豫不决。
这篇文章在code-project上应该能帮助你入门。

-1

-1
其他答案缺少适当的清理:当计时器在窗口关闭的确切时刻触发时,尝试运行Dispatcher.Invoke时会出现未捕获的TaskCanceledException异常。我在其他问题中没有找到解决此问题的帮助。我能够通过在关闭窗口时取消注册计时器回调来解决这个问题。
public partial class MainWindow : Window
{
    Timer clockTimer = null;
    public MainWindow()
    {
        clockTimer = new Timer(1.0); // 1 ms update to test for TaskCanceledException 
        clockTimer.Elapsed += Timer_Elapsed;
        clockTimer.AutoReset = true;
        clockTimer.Start();
        Closed += (object sender, EventArgs e) => { clockTimer.Elapsed -= Timer_Elapsed; };
    }
    private void Timer_Elapsed(object sender, ElapsedEventArgs e) {
        var now = DateTime.Now;
        Dispatcher.Invoke((Action)delegate () {
            UpdateTime(now);
        });
    }
}

如果窗口重新显示,显然这不是一个好主意。我尝试添加了一个析构函数,但它永远不会被调用,可能是由于循环依赖。

免责声明:我不懂C#,所以这可能不是最好或正确的做法。


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