这是我想做的事情的简化版本:
onClick abutton
, a aNewMethod()
将异步运行以保持 UI 响应。就是这样!
我读过一些答案,这是我能想到的:
private async void button_Click(object sender, RoutedEventArgs e)
{
Task task = Task.Run(() => aNewMethod());
await task;
}
private void aNewMethod()
{
if (progress.Value == 0)
{
//Heavy work
for (int i = 1; i < 1000000000; i++) { }
progress.Value = 100;
}
}
正如您可能已经想到的,这会引发System.InvalidOperationException
at if(progress.Value== 0)
说:
调用线程无法访问此对象,因为不同的
线程拥有它。
经过一番谷歌搜索后,我读到我需要一个Dispatcher.BeginInvoke()
更新/使用 UI 控件的方法,所以我这样做了:
private void aNewMethod()
{
Dispatcher.BeginInvoke((Action)delegate {
if (progress.Value == 0)
{
//Heavy work
for (int i = 1; i < 1000000000; i++) { }
progress.Value = 100;
}
});
}
这解决了System.InvalidOperationException
但它不是异步运行,因为 UI 仍然冻结在for loop
所以问题是:如何运行aNewMethod();
异步并且仍然更新并与 UI 控件交互?
调度程序在 UI 线程中运行。它处理您的 UI,并执行您通过 BeginInvoke 等传递给它的操作。
但它一次只能处理一件事;当它忙于处理您的操作时,它不会同时更新 UI,因此 UI 会同时冻结。
如果您希望保持 UI 响应能力,则需要在单独的非 UI 线程中运行重负载函数。在其他线程上运行的这些函数中,只要需要访问 UI,您就可以调用调度程序 - 理想情况下,只需非常短暂地更新 UI 元素。
换句话说,您希望在单独的线程中运行睡眠函数,然后在需要设置进度值时从您自己的线程调用调度程序。就像是
private void button_Click(object sender, RoutedEventArgs e)
{
Task task = Task.Run(() => aNewMethod()); // will call aNewMethod on the thread pool
}
private void aNewMethod()
{
double progressValue = 0;
Dispatcher.Invoke(() => progressValue = progress.Value);
if (progressValue == 0)
{
Thread.Sleep(3000); // still executes on the threadpool (see above), so not blocking UI
Dispatcher.Invoke(() => progress.Value = 100 ); // call the dispatcher to access UI
}
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)