从异步方法更新SystemTray ProgressIndicator值

时间:2013-09-12 07:55:11

标签: c# mvvm windows-phone-8 async-await

我有一个长时间运行的后台操作,用于将用户数据与远程Web服务同步,并希望向用户提供有关该操作状态的反馈。 SystemTray ProgressIndicator在XAML中绑定到ViewModel上的属性,如下所示:

<shell:SystemTray.ProgressIndicator>
    <shell:ProgressIndicator IsVisible="{Binding IsSynchronizing, Mode=OneWay}"
                             Text="Synchronizing..."
                             Value="{Binding SyncProgress, Mode=OneWay}" />
</shell:SystemTray.ProgressIndicator>

这里的绑定在调用开始时正常工作。从ViewModel调用我的异步Web服务操作,并为其提供回调,以根据操作的当前状态设置其属性的值:

RemoteServiceManager.Current.UpdateUserDataAsync((progress) =>
{
    // Marshal to UI thread
    Deployment.Current.Dispatcher.BeginInvoke(() =>
    {
        IsSynchronizing = !progress.OperationFinished;
        SyncProgress = progress.PercentComplete;

        if (progress.OperationFinished)
        {
            OnDataChanged();
        }
    });
});

这种方法的问题在于UI调度程序似乎永远不会调用该操作(setter永远不会被调用),如果我删除对调度程序的调用并直接在委托中执行代码,代码将在不生成的情况下执行例外,但进度指示器上的绑定属性不会使用新的进度值更新。如何从这个长时间运行的异步操作中正确更新ProgressInidcator Value属性?

另一个简短的最后一个问题:我在ProgressIndicator上设置的值是否应该在0-1或0-100范围内?

2 个答案:

答案 0 :(得分:1)

据我所知,调度程序不会调用您的操作的唯一情况是UI线程已经忙碌。

调用异步方法时,将捕获当前的同步上下文。异步调用完成后,在捕获的同步上下文上执行await语句后的代码。例如,同步上下文是UI线程。因此,重要的是要记住,如果您在调用await之前进入UI线程,则在调用完成后您仍将处于该UI线程中。

此外,将任务视为一个线程是一个常见的错误。任务只是一个工作单元。它不一定在单独的线程中执行。实际上,如果你编写一个异步方法但从不启动一个新线程,那么你的方法将在当前线程中执行。为什么这样做?仅仅因为你并不总是需要一个异步的线程。例如,如果您只是订阅I / O事件,那么创建一个线程将浪费资源。

答案 1 :(得分:0)

您可以尝试创建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线程上调用。

我希望这会有所帮助。