1

ユーザー データをリモート 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>

ここでのバインディングは、呼び出しの開始時に正しく機能します。非同期 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 Value プロパティを適切に更新するにはどうすればよいですか?

もう 1 つの簡単な最後の質問: ProgressIndicator に設定する値は 0 ~ 1 または 0 ~ 100 の範囲にする必要がありますか?

4

2 に答える 2

1

私の知る限り、ディスパッチャーがアクションを呼び出さない唯一のケースは、UI スレッドが既にビジー状態の場合です。

非同期メソッドを呼び出すと、現在の同期コンテキストがキャプチャされます。非同期呼び出しが完了すると、awaitステートメントの後のコードがキャプチャされた同期コンテキストで実行されます。同期コンテキストは、たとえば UI スレッドです。したがって、 の呼び出し前にawaitUI スレッドにいた場合は、呼び出しが完了したときにもその UI スレッドにいることを覚えておくことが重要です。

また、タスクをスレッドと考えるのはよくある間違いです。タスクは単なる作業単位です。必ずしも別のスレッドで実行されるわけではありません。実際、非同期メソッドを作成しても新しいスレッドを開始しない場合、メソッドは現在のスレッドで実行されます。なぜそのようにされるのですか?スレッドを常に非同期にする必要があるとは限らないからです。たとえば、I/O イベントをサブスクライブしているだけの場合、スレッドを作成するとリソースが無駄になります。

于 2013-09-12T15:45:43.353 に答える
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 スレッドで自動的に呼び出されます。

これが役立つことを願っています。

于 2013-09-12T12:49:45.037 に答える