在运行方法的其余部分之前更新 UI
本文关键字:更新 UI 余部 运行 方法 | 更新日期: 2023-09-27 18:33:59
我有一个长时间运行的操作,必须在 UI 线程中完成(涉及无法冻结的 UI 元素)。我想在运行操作之前显示一个忙指示器。
busyIndicator.Visibility = Visibility.Visible;
LongRunningMethod();
busyIndicator.Visibility = Visibility.Collapsed;
当然,这不起作用,因为在操作完成之前不会进行渲染。我尝试使用 Task.Yield() 异步运行方法的其余部分:
busyIndicator.Visibility = Visibility.Visible;
await Task.Yield();
LongRunningMethod();
据我了解,这也不起作用,因为该方法的其余部分的优先级高于渲染操作。
如何使用 TPL 执行此操作?
UPD:LongRunningMethod
本质上不能在单独的线程中运行(适用于复杂的 WPF 3D 模型),无论如何,我现在无法对其进行更改。因此,请不要提供基于完全或部分在单独线程上运行的解决方案。
如果要中断UI方法执行,则必须使用async
/await
。
类似的东西(未经测试)
busyIndicator.Visibility = Visibility.Visible;
await Task.Run(() => await Task.Delay(1)); // here method will exit and repaint will occurs
LongRunningMethod();
busyIndicator.Visibility = Visibility.Collapsed;
但是根据方法运行的时间长短,您可能希望将其完全放入另一个线程(Task
,BackgroundWorker
),并且仅在需要进入UI线程时才调用方法。
当您学习如何异步和等待工作时,这是非常微不足道的。您可以在 msdn 上阅读有关此内容的信息
您的问题是您永远不会开始任何异步工作 - Task.Yield
已经从 UI 上下文中调用并且不会执行任何操作。
试试这个:
async Task MyProcess() //the Task is returned implicitly
{
busyIndicator.Visibility = Visibility.Visible;
await LongRunningMethod(); //the work here is done in a worker thread
busyIndicator.Visibility = Visibility.Collapsed; //this line executes back on the ui context
}
Task LongRunningMethod() //the Async suffix is a convention to differentiate overloads that return Tasks
{
var result1 = await Task.Run(() => /* do some processing */ ); //we are actually starting an asynchronous task here.
//update some ui elements here
var result2 = await Task.Run(() => /* do some more processing */ );
//update some ui elements here
}
假设你的长时间运行的方法不是async
你应该能够在Task.Run
内部调用它并await
结果,或者更好地使用TaskFactory.StartNew
并传入TaskCreationOptions.LongRunning
。
busyIndicator.Visibility = Visibility.Visible;
await TaskFactory.StartNew(() => LongRunningMethod(),TaskCreationOptions.LongRunning);
busyIndicator.Visibility = Visibility.Collapsed;
附带说明一下,您需要小心Task.Yield
因为在某些情况下(例如WindowsForms
SynchronizationContext
上计划的任务比重新绘制窗口具有更高的优先级,因此,如果您反复调用 Task.Yield
,例如在 while 循环中,可以使 UI 无响应。