从异步方法更新系统托盘进度指示器值
本文关键字:指示器 异步方法 更新 系统 | 更新日期: 2023-09-27 18:30:58
我有一个长时间运行的后台操作,用于将用户数据与远程 Web 服务同步,并希望向用户提供有关该操作状态的反馈。SystemTray 进度指示器在 XAML 中绑定到 ViewModel 上的属性,如下所示:
<shell:SystemTray.ProgressIndicator>
<shell:ProgressIndicator IsVisible="{Binding IsSynchronizing, Mode=OneWay}"
Text="Synchronizing..."
Value="{Binding SyncProgress, Mode=OneWay}" />
</shell:SystemTray.ProgressIndicator>
此处的绑定在调用开始时正常工作。我的异步 Web 服务操作是从 ViewModel 调用的,我为它提供了一个回调,以根据操作的当前状态设置其属性的值:
RemoteServiceManager.Current.UpdateUserDataAsync((progress) =>
{
// Marshal to UI thread
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
IsSynchronizing = !progress.OperationFinished;
SyncProgress = progress.PercentComplete;
if (progress.OperationFinished)
{
OnDataChanged();
}
});
});
这种方法的问题在于 UI 调度程序似乎永远不会调用操作(资源库永远不会被调用),如果我删除对调度程序的调用并直接执行委托中的代码,代码将执行而不会生成异常,但进度指示器上的绑定属性不会使用新的进度值更新。如何从此长时间运行的异步操作正确更新 ProgressInidcator 值属性?
另一个简短的最后一个问题:我在进度指示器上设置的值应该介于 0-1 还是 0-100 之间?
据我所知,调度程序不会调用您的操作的唯一情况是 UI 线程已经繁忙。
调用异步方法时,将捕获当前同步上下文。异步调用完成后,将在捕获的同步上下文上执行 await
语句后的代码。例如,同步上下文是 UI 线程。因此,请务必记住,如果在调用 await
之前处于 UI 线程中,则在调用完成后仍会位于该 UI 线程中。
此外,将任务视为线程是一个常见的错误。任务只是一个工作单元。它不一定在单独的线程中执行。事实上,如果您编写了一个异步方法,但从不启动新线程,那么您的方法将在当前线程中执行。为什么要这样做?仅仅因为您并不总是需要线程是异步的。例如,如果您只是订阅 I/O 事件,则创建线程将浪费资源。
您可以尝试创建一个IProgress<T>
对象
IProgress<ProgressInfo> progressIndicator = new Progress<ProgressInfo>(ReportProgress);
并将其传递到您的async
方法
public async void YourMethodAsync(IProgress<ProgressInfo> progressIndicator)
{
ProgressInfo pi = new ProgressInfo(0);
...
// Report progress.
pi.PrecentageComplete = 30;
progressIndicator.Report(pi);
...
}
哪里
public class ProgressInfo
{
private int progressPercentage;
public ProgressInfo() {}
public ProgressInfo(int progressPercentage)
{
this.progressPercentage = progressPercentage;
}
public int ProgressPercentage
{
get { return progressPercentage; }
set { progressPercentage = value; }
}
}
ReportProgress
方法是这样的
public void ReportProgress(CostEngine.ProgressInfo progressInfo)
{
SyncProgress = progressInfo.PercentComplete;
}
将在 UI 线程上自动调用。
我希望这有所帮助。