wpf 如何使用异步连续更新 GUI
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/38638911/
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
How to update GUI continuously with async
提问by MonsterMMORPG
Just created a WPF project .net 4.6
刚刚创建了一个 WPF 项目 .net 4.6
And have put this code inside
并把这段代码放在里面
lbl1is a label on the GUI
lbl1是 GUI 上的标签
But the label is never updated or the while loop continue only 1 time
但标签永远不会更新或 while 循环只继续 1 次
private void Button_Click_1(object sender, RoutedEventArgs e)
{
var t = Task.Run(
async () =>
{
await AsyncLoop();
});
}
async Task AsyncLoop()
{
while (true)
{
string result = await LoadNextItem();
lbl1.Content = result;
}
}
private static int ir11 = 0;
async Task<string> LoadNextItem()
{
ir11++;
return "aa " + ir11;
}
采纳答案by suulisin
Please user Application.Current.Dispatcher.Invoke to access the ui thread
请使用 Application.Current.Dispatcher.Invoke 访问 ui 线程
async Task AsyncLoop()
{
while (true)
{
string result = await LoadNextItem();
Application.Current.Dispatcher.Invoke(new Action(() => { lbl1.Content = result; }));
}
}
回答by Stephen Cleary
The C# compiler is giving you a warning that tells you what the problem is.
C# 编译器向您发出警告,告诉您问题所在。
Specifically, this method is not asynchronous:
具体来说,这个方法不是异步的:
async Task<string> LoadNextItem()
{
ir11++;
return "aa " + ir11;
}
The compiler message will inform you that this asyncmethod has no awaitstatements, and thus will run synchronously. You should only use asyncwhere it makes sense, usually for I/O-based operations, e.g.:
编译器消息会通知您该async方法没有await语句,因此将同步运行。您应该只async在有意义的地方使用,通常用于基于 I/O 的操作,例如:
async Task<string> LoadNextItemAsync()
{
await Task.Delay(100); // Placeholder for actual asynchronous work.
ir11++;
return "aa " + ir11;
}
Alternatively, if you don'thave asynchronous operations, but rather you have some tight CPU-bound loops, then you can push those off to a background thread via Task.Run:
或者,如果您没有异步操作,而是有一些紧密的 CPU 绑定循环,那么您可以通过Task.Run以下方式将它们推送到后台线程:
string LoadNextItem()
{
ir11++;
return "aa " + ir11;
}
while (true)
{
string result = await Task.Run(() => LoadNextItem());
lbl1.Content = result;
}
回答by SensorSmith
By invoking Task.Run you broke your association(SynchronizationContext) with the GUI thread (or WPF Dispatcher) and lost most of the async/await 'goodness'.
通过调用 Task.Run,您打破了与 GUI 线程(或 WPF 调度程序)的关联(SynchronizationContext)并失去了大部分异步/等待“优点”。
Why not use an async void event handler and just come back to the SynchronizationContext(GUI Thread/Dispatcher) for each step?
为什么不使用 async void 事件处理程序,然后在每一步返回 SynchronizationContext(GUI Thread/Dispatcher)?
private async void Button_Click_1(object sender, RoutedEventArgs e)
{
while (true)
{
string result = await LoadNextItem();
lbl1.Content = result;
}
}
private static int ir11 = 0;
Task<string> LoadNextItem()
{
await Task.Delay(1000); // placeholder for actual async work
ir11++;
return "aa " + ir11;
}
Or if you really want to separate the state machine for the 'on-going' operations, try passing an IProgress<T>(the default impl. Progress<T>or specifically Progress<string>should work great in this case). See this article by @Stephen Cleary
或者,如果您真的想为“正在进行的”操作分离状态机,请尝试传递一个IProgress<T>(默认实现。Progress<T>或者Progress<string>在这种情况下特别应该工作得很好)。请参阅@Stephen Cleary 撰写的这篇文章
His example is very close to what you stated in the question. I've copied it here for SO independence.
他的例子非常接近你在问题中所说的。我已将其复制到此处以保持独立性。
public async void StartProcessingButton_Click(object sender, EventArgs e)
{
// The Progress<T> constructor captures our UI context,
// so the lambda will be run on the UI thread.
var progress = new Progress<int>(percent =>
{
textBox1.Text = percent + "%";
});
// DoProcessing is run on the thread pool.
await Task.Run(() => DoProcessing(progress));
textBox1.Text = "Done!";
}
public void DoProcessing(IProgress<int> progress)
{
for (int i = 0; i != 100; ++i)
{
Thread.Sleep(100); // CPU-bound work
if (progress != null)
progress.Report(i);
}
}
Edit:I must admit, while Progress<T>is a nice abstraction, in this case it is just going to fall down to Dispatcher.Invoke as @Pamparanpa suggested.
编辑:我必须承认,虽然Progress<T>是一个很好的抽象,但在这种情况下,它只会像@Pamparanpa 建议的那样归结为 Dispatcher.Invoke。

