使用Dispatcher.Invoke从非主线程更改WPF控件

时间:2009-10-29 14:20:46

标签: c# wpf multithreading dispatcher

我最近开始在WPF编程并遇到了以下问题。我不明白如何使用Dispatcher.Invoke()方法。我有线程经验,我做了一些简单的Windows窗体程序,我只使用了

Control.CheckForIllegalCrossThreadCalls = false;

是的我知道这很蹩脚,但这些都是简单的监控应用程序。

现在的事实是我正在制作一个WPF应用程序,它在后台检索数据,我开始一个新的线程来调用来检索数据(来自网络服务器),现在我想在我的WPF表单上显示它。问题是,我不能从这个线程设置任何控制。甚至没有标签或任何东西。怎么解决这个问题?

回答评论:
@Jalfp:
 所以当我得到数据时,我会在'new tread'中使用这个Dispatcher方法吗?或者我应该让后台工作者检索数据,将其放入一个字段并启动一个等待该字段填满的新线程并调用调度程序将检索到的数据显示到控件中?

4 个答案:

答案 0 :(得分:163)

首先要了解的是,Dispatcher并非设计用于运行长阻塞操作(例如从WebServer检索数据......)。如果要运行将在UI线程上执行的操作(例如更新进度条的值),可以使用Dispatcher。

您可以做的是在后台工作程序中检索数据,并使用ReportProgress方法在UI线程中传播更改。

如果你真的需要直接使用Dispatcher,那很简单:

Application.Current.Dispatcher.BeginInvoke(
  DispatcherPriority.Background,
  new Action(() => this.progressBar.Value = 50));

答案 1 :(得分:25)

japf已正确回答。如果您正在查看多行操作,可以按如下方式编写。

Application.Current.Dispatcher.BeginInvoke(
  DispatcherPriority.Background,
  new Action(() => { 
    this.progressBar.Value = 50;
  }));

想要了解效果的其他用户的信息:

如果您的代码需要为高性能而编写,您可以先使用CheckAccess标志检查是否需要调用。

if(Application.Current.Dispatcher.CheckAccess())
{
    this.progressBar.Value = 50;
}
else
{
    Application.Current.Dispatcher.BeginInvoke(
      DispatcherPriority.Background,
      new Action(() => { 
        this.progressBar.Value = 50;
      }));
}

请注意,方法CheckAccess()在Visual Studio 2015中是隐藏的,因此只需编写它而不需要智能感知来显示它。请注意,CheckAccess的性能开销(开销只有几纳秒)。只有当您想要保存执行“调用”所需的微秒时,它才会更好。不惜一切代价。此外,当调用方法确定它是否在UI线程中时,总是有选项可以创建两个方法(on with invoke,其他没有)。当您应该关注调度员的这一方面时,这是罕见的罕见案例。

答案 2 :(得分:1)

当一个线程正在执行并且您想要执行被当前线程阻止的主UI线程时,请使用以下命令:

当前线程:

Dispatcher.CurrentDispatcher.Invoke(MethodName,
    new object[] { parameter1, parameter2 }); // if passing 2 parameters to method.

主UI线程:

Application.Current.Dispatcher.BeginInvoke(
    DispatcherPriority.Background, new Action(() => MethodName(parameter)));

答案 3 :(得分:0)

上面的@japf答案工作正常,我想将鼠标光标从纺车更改为普通的箭头,一旦 CEF浏览器完成了页面的加载。如果可以帮助某人,则代码如下:

private void Browser_LoadingStateChanged(object sender, CefSharp.LoadingStateChangedEventArgs e) {
   if (!e.IsLoading) {
      // set the cursor back to arrow
      Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background,
         new Action(() => Mouse.OverrideCursor = Cursors.Arrow));
   }
}