C# 使用 Dispatcher.Invoke 从非主线程更改 WPF 控件
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1644079/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Change WPF controls from a non-main thread using Dispatcher.Invoke
提问by D. Veloper
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
我最近开始在 WPF 中编程并遇到以下问题。我不明白如何使用该Dispatcher.Invoke()
方法。我在线程方面有经验,并且我制作了一些简单的 Windows 窗体程序,其中我刚刚使用了
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?
事实是现在我正在制作一个在后台检索数据的 WPF 应用程序,我启动了一个新线程来调用检索数据(从网络服务器),现在我想在我的 WPF 表单上显示它。问题是,我无法从该线程设置任何控制。甚至没有标签或任何东西。如何解决这个问题?
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?
回答评论:
@Jalfp:
所以当我获取数据时,我会在“新胎面”中使用这个 Dispatcher 方法?或者我应该让后台工作人员检索数据,将其放入一个字段并启动一个新线程,等待该字段被填充并调用调度程序将检索到的数据显示到控件中?
采纳答案by japf
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).
首先要了解的是,Dispatcher 并不是为运行长时间阻塞操作而设计的(例如从 WebServer 检索数据...)。当您想要运行将在 UI 线程上执行的操作(例如更新进度条的值)时,您可以使用 Dispatcher。
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.
您可以做的是在后台工作程序中检索您的数据并使用 ReportProgress 方法在 UI 线程中传播更改。
If you really need to use the Dispatcher directly, it's pretty simple:
如果你真的需要直接使用 Dispatcher,这很简单:
Application.Current.Dispatcher.BeginInvoke(
DispatcherPriority.Background,
new Action(() => this.progressBar.Value = 50));
回答by Yogee
japf has answer it correctly. Just in case if you are looking at multi-line actions, you can write as below.
japf 已正确回答。以防万一,如果您正在查看多行操作,您可以编写如下。
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.
如果您的代码需要编写以获得高性能,您可以首先使用 CheckAccess 标志检查是否需要调用。
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.
请注意,方法 CheckAccess() 对 Visual Studio 2015 是隐藏的,因此只需编写它,而无需期望智能感知显示它。请注意,CheckAccess 有性能开销(几纳秒内的开销)。当您想不惜一切代价节省执行“调用”所需的微秒时,它只会更好。此外,当调用方法确定它是否在 UI 线程中时,总是可以选择创建两个方法(使用 invoke,其他不使用)。当您应该查看调度程序的这一方面时,这只是最罕见的情况。
回答by Gopi Rao
When a thread is executing and you want to execute the main UI thread which is blocked by current thread, then use the below:
当一个线程正在执行并且您想要执行被当前线程阻塞的主 UI 线程时,请使用以下命令:
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)));
回答by nrod
The @japf answer above is working fine and in my case I wanted to change the mouse cursor from a Spinning Wheelback to the normal Arrowonce the CEF Browserfinished loading the page. In case it can help someone, here is the code:
上面的@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));
}
}