Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
259 views
in Technique[技术] by (71.8m points)

c# - How can I efficiently update the UI from an async method?

I'm using a ProgressBar with binding to show the progress when receiving a file from a remote device.

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

ProgressFileReceive is a property (double) in my View Model which has the percentage completion. So to update the Progress Bar, I add to this number.

The problem is I have the file transfer method in a different async method, and so to access this property I must use the following code :

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

This works but makes the whole process extremely slow, since at each iteration (there are over a thousand loops since I read byte-by-byte) the method has to use the dispatcher to update the UI. It takes several times longer to receive the whole file, than it would take if I was not updating the UI.

How can I do this more efficiently so as to speed up the process ?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The problem is I have the file transfer method in a different async method

That doesn't necessarily follow. You shouldn't need to use CoreDispatcher explicitly. Asynchronous methods resume on the UI thread by default.


For progress reporting, you should use IProgress<T>. You can use it with a structure to report progress, as such:

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
    });
  }
  ...
}

Then you can consume it with an IProgress<T> implementation. Since you need UI throttling, you can use one that I wrote that has built-in throttling:

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...