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

发布于 2024-08-09 19:25:58 字数 506 浏览 7 评论 0原文

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

Control.CheckForIllegalCrossThreadCalls = false;

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

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

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

I have recently started programming in WPF and bumped into the following problem. I don't understand how to use the Dispatcher.Invoke() method. I have experience in threading and I have made a few simple Windows Forms programs where I just used the

Control.CheckForIllegalCrossThreadCalls = false;

Yes I know that is pretty lame but these were simple monitoring applications.

The fact is now I am making a WPF application which retrieves data in the background, I start off a new thread to make the call to retrieve the data (from a webserver), now I want to display it on my WPF form. The thing is, I cannot set any control from this thread. Not even a label or anything. How can this be resolved?

Answer comments:
@Jalfp:
So I use this Dispatcher method in the 'new tread' when I get the data? Or should I make a background worker retrieve the data, put it into a field and start a new thread that waits till this field is filled and call the dispatcher to show the retrieved data into the controls?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

不寐倦长更 2024-08-16 19:25:58

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

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

如果您确实需要直接使用 Dispatcher,那非常简单:

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

The first thing is to understand that, the Dispatcher is not designed to run long blocking operation (such as retrieving data from a WebServer...). You can use the Dispatcher when you want to run an operation that will be executed on the UI thread (such as updating the value of a progress bar).

What you can do is to retrieve your data in a background worker and use the ReportProgress method to propagate changes in the UI thread.

If you really need to use the Dispatcher directly, it's pretty simple:

Application.Current.Dispatcher.BeginInvoke(
  DispatcherPriority.Background,
  new Action(() => this.progressBar.Value = 50));
难以启齿的温柔 2024-08-16 19:25:58

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

japf has answer it correctly. Just in case if you are looking at multi-line actions, you can write as below.

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

Information for other users who want to know about performance:

If your code NEED to be written for high performance, you can first check if the invoke is required by using CheckAccess flag.

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

Note that method CheckAccess() is hidden from Visual Studio 2015 so just write it without expecting intellisense to show it up. Note that CheckAccess has overhead on performance (overhead in few nanoseconds). It's only better when you want to save that microsecond required to perform the 'invoke' at any cost. Also, there is always option to create two methods (on with invoke, and other without) when calling method is sure if it's in UI Thread or not. It's only rarest of rare case when you should be looking at this aspect of dispatcher.

满天都是小星星 2024-08-16 19:25:58

当一个线程正在执行并且您想要执行被当前线程阻塞的主 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)));

When a thread is executing and you want to execute the main UI thread which is blocked by current thread, then use the below:

current thread:

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

Main UI thread:

Application.Current.Dispatcher.BeginInvoke(
    DispatcherPriority.Background, new Action(() => MethodName(parameter)));
风蛊 2024-08-16 19:25:58

上面的@japf答案工作正常,在我的例子中,一旦CEF浏览器<,我想将鼠标光标从旋转轮更改回正常的箭头 /strong> 完成页面加载。如果它可以帮助某人,这里是代码:

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));
   }
}

The @japf answer above is working fine and in my case I wanted to change the mouse cursor from a Spinning Wheel back to the normal Arrow once the CEF Browser finished loading the page. In case it can help someone, here is the code:

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));
   }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文