在运行方法的其余部分之前更新 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

如果要中断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;

但是根据方法运行的时间长短,您可能希望将其完全放入另一个线程(TaskBackgroundWorker),并且仅在需要进入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 无响应。