如何从异步方法高效地更新UI?

3

我正在使用ProgressBar并绑定来显示从远程设备接收文件时的进度。

<ProgressBar Width="500" Height="50" Value="{Binding ProgressFileReceive}"/>

ProgressFileReceive是我视图模型的一个属性(double),表示完成的百分比。因此,为了更新进度条,我会将这个数字加上去。

问题在于,我的文件传输方法在另一个async方法中,所以要访问这个属性,我必须使用以下代码 :

await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal,
   () =>
   {
       // do something on the UI thread
       ProgressFileReceive = (double)i / fileSize * 100;
   });

这样可以工作,但会让整个过程变得非常缓慢,因为在每次迭代(由于我是逐字节读取),该方法必须使用调度程序来更新UI。接收整个文件所需的时间比如果我不更新UI要长几倍。

如何更有效地进行操作以加快进程?


一种简单的方法是:每X次迭代更新一次进度条,其中X足够大以不会过多减慢处理速度,但又不要太大以使进度条变得不连续。 - stuartd
@Aniruddha Varma,你不需要手动将绑定的数据封送到UI线程,因为你使用了绑定。WPF会自动处理它。 - Artavazd Balayan
@stuartd 谢谢。是的,我会加一个计数器,只要它看起来比较平滑,那应该就可以了。 - Arctic Vowel
@ArtavazdBalayan 如果在 RaisePropertyChanged 中设置访问器引发异常,那么它就是一个 Windows Store (Metro) 应用程序。 - Arctic Vowel
@Aniruddha Varma,天啊,谢谢,我不知道这件事。 - Artavazd Balayan
1个回答

3
问题在于我将文件传输方法放在了另一个异步方法中。
这并不一定正确。你不需要显式地使用CoreDispatcher,异步方法默认会在UI线程上继续执行。
为了报告进度,你应该使用IProgress<T>。你可以使用它与结构一起来报告进度,如下所示:
public struct ProgressReport
{
  public double Progress { get; set; }
  public double FileSize { get; set; }
}

async Task FileTransferAsync(IProgress<ProgressReport> progress)
{
  ...
  if (progress != null)
  {
    progress.Report(new ProgressReport
    {
      Progress = (double)i,
      FileSize = fileSize
    });
  }
  ...
}

接下来,您可以使用 IProgress<T> 实现来消费它。由于需要 UI 节流,因此您可以使用我编写的内置节流的一个

using (var progress = ObservableProgress<ProgressReport>.CreateForUi(value =>
    {
        ProgressFileReceive = (double)value.Progress / value.FileSize * 100;
    }))
{
    await FileTransferAsync(progress);
}

谢谢。我尝试了一下,但在ProgressFileReceive的set访问器中的RaisePropertyChanged()方法内部出现了“COMException - 应用程序调用了一个为不同线程编组的接口”的错误。 - Arctic Vowel
@AniruddhaVarma:你必须在UI线程上构建ObservableProgress - Stephen Cleary
我明白了。但是任务必须从异步方法的范围内开始,所以我将您的第二段代码包装在CoreWindow.Dispatcher中,以便从UI线程中运行它。这不会引发任何错误,但进度条仅在任务结束时更新,直接跳到100%。 - Arctic Vowel
@AniruddhaVarma:在移动到后台线程之前,您应该创建ObservableProgress。您根本不需要使用调度程序。 - Stephen Cleary

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接