【问题标题】:Refresh TextBlock quicker from another thread从另一个线程更快地刷新 TextBlock
【发布时间】:2016-02-19 10:33:28
【问题描述】:

我确实有一个 WPF 应用程序实时显示数据(从另一个线程计算)。但是我的 UI 组件(这里是 TextBlock)更新非常缓慢。 我使用带有PropertyChanged 通知的传统数据绑定。 xml:

<TextBlock
          Foreground="DarkGray"
          Text="{Binding Path=ContactSurface, StringFormat='{}{0:0.00} cm²'}"/>

代码隐藏(不,这不是 MVVM,真丢脸):

private double _contactSurface;
    public double ContactSurface
    {
        get { return _contactSurface; }
        set { _contactSurface = value; RaisePropertyChanged("ContactSurface"); }
    }

    public void Compute() // external thread about 10 Hz
    {
        ContactSurface = (double)nbSensorsNotNulls * DataSource.SensorSurface * 0.01;

        Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Render, new Action(() => { })); // does not change a thing
        //Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Render, new Action(() => { })); // crash : Cannot perform this operation while dispatcher processing is suspended.

        //UpdateLayout(); // crash : The calling thread can not access this object because a different thread owns it
        //InvalidateVisual(); // crash : The calling thread can not access this object because a different thread owns it

    }

我在Compute() 结尾处尝试了一些我在网络上找到的东西,结果非常简单

【问题讨论】:

  • 也许你应该把 ContactSurface = (double)nbSensorsNotNulls * DataSource.SensorSurface * 0.01 放入 Dispatcher.BeginInvoke
  • 这似乎行得通!我不确定是什么问题。也许 RaisePropertyChanged() 没有从 Compute 线程同步,所以调度程序不知道 ContactSurface 发生了变化。非常感谢!!!
  • 这种方法有一个不便之处:它往往会冻结 UI。我正在努力。

标签: wpf multithreading data-binding


【解决方案1】:

如果您在另一个线程中执行一些耗时的工作,那么您必须将另一个线程的结果与 UI 线程同步。要同步两个线程(新线程和UI线程),需要使用Dispatcher

As MSDN says:

只有一个线程可以修改 UI 线程。但是后台线程如何交互 与用户?后台线程可以要求 UI 线程执行 代其操作。它通过注册一个工作项来做到这一点 UI 线程的调度程序。 Dispatcher 类提供了两个 注册工作项的方法:Invoke 和 BeginInvoke。两个都 方法安排一个委托执行。调用是同步的 调用——也就是说,它直到 UI 线程实际才返回 完成执行委托。 BeginInvoke 是异步的,并且 立即返回。

例如:

Task.Run(()=> {
   var result = (double)nbSensorsNotNulls * DataSource.SensorSurface * 0.01;
   Thread.Sleep(5000);//imitate time consuming work
   Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Render,
            new Action(() => { 
                            ContactSurface=result;}));
});

在上面的例子中,我们创建了一个新线程(Task.Run(...))并将新线程的结果与UI线程(Dispatcher.BeginInvoke(...))同步

【讨论】:

  • 谢谢,这更好,因为 Action() 内部的计算更少。我的用户界面现在可能会冻结一点。我想我必须限制 Dispatcher 计算和 Dispatcher 调用的 mb 频率?
  • @olivier 你是对的!您只需将 非 UI 线程的结果 插入 Dispatcher。请阅读这篇文章,您将获得非常有用的知识:msdn.microsoft.com/library/ms741870(v=vs.100).aspx
  • @olivier 请记住通过将有用的帖子标记为答案来关闭您的线程,然后如果您有新问题,请启动一个新线程。请不要在同一个线程中问多个问题。 meta.stackexchange.com/questions/5234/…
猜你喜欢
  • 1970-01-01
  • 2017-01-04
  • 1970-01-01
  • 1970-01-01
  • 2023-03-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多