88

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

Control.CheckForIllegalCrossThreadCalls = false;

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

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

回答评论:
@Jalfp:
所以当我获取数据时,我在“新胎面”中使用了这个 Dispatcher 方法?或者我应该让后台工作人员检索数据,将其放入一个字段并启动一个新线程,等待该字段被填充并调用调度程序将检索到的数据显示到控件中?

4

4 回答 4

188

首先要理解的是,Dispatcher 并非旨在运行长时间阻塞操作(例如从 WebServer 检索数据......)。当您想要运行将在 UI 线程上执行的操作(例如更新进度条的值)时,可以使用 Dispatcher。

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

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

Application.Current.Dispatcher.BeginInvoke(
  DispatcherPriority.Background,
  new Action(() => this.progressBar.Value = 50));
于 2009-10-29T14:46:41.993 回答
33

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 线程中时,总是可以选择创建两个方法(使用调用,其他不使用)。当您应该查看调度程序的这一方面时,这只是最罕见的情况。

于 2015-08-30T16:02:40.220 回答
4

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

当前线程:

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

主界面线程:

Application.Current.Dispatcher.BeginInvoke(
    DispatcherPriority.Background, new Action(() => MethodName(parameter)));
于 2018-11-15T01:25:24.703 回答
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));
   }
}
于 2020-02-13T11:08:34.407 回答