MVVMアプリケーションでは、私のビューモデルは3つの異なるサービスメソッドを呼び出し、それぞれのデータを共通のフォーマットに変換し、プロパティ通知/観測可能なコレクションなどを使用してUIを更新します。UIスレッドをブロックせずに複数のタスクを続行するにはどうすればよいですか?
サービスレイヤの各メソッドは、 Task
を返し、ビューモデルにTask
を返します。ここに私のサービス方法の例があります。
public class ResourceService
{
internal static Task LoadResources(Action<IEnumerable<Resource>> completedCallback, Action<Exception> errorCallback)
{
var t = Task.Factory.StartNew(() =>
{
//... get resources from somewhere
return resources;
});
t.ContinueWith(task =>
{
if (task.IsFaulted)
{
errorCallback(task.Exception);
return;
}
completedCallback(task.Result);
}, TaskScheduler.FromCurrentSynchronizationContext());
return t;
}
}
ここでは、呼び出し元のコードとビューモデルの他の関連する部分は、これはほとんどの作品は...
private ObservableCollection<DataItem> Data = new ObservableCollection<DataItem>();
public ICollectionView DataView
{
get { return _dataView; }
set
{
if (_dataView != value)
{
_dataView = value;
RaisePropertyChange(() => DataView);
}
}
}
private void LoadData()
{
SetBusy("Loading...");
Data.Clear();
Task[] tasks = new Task[3]
{
LoadTools(),
LoadResources(),
LoadPersonel()
};
Task.WaitAll(tasks);
DataView = CollectionViewSource.GetDefaultView(Data);
DataView.Filter = FilterTimelineData;
IsBusy = false;
}
private Task LoadResources()
{
return ResourceService.LoadResources(resources =>
{
foreach(var r in resources)
{
var d = convertResource(r);
Data.Add(d);
}
},
error =>
{
// do some error handling
});
}
ですが、小さな問題がいくつかあります。
番号1:最初にSetBusy
を呼び出すときに、私がタスクを開始する前に、WaitAll
を呼び出す前に、IsBusy
プロパティをtrueに設定します。 UIを更新し、BusyIndicatorコントロールを表示する必要がありますが、機能していません。また、単純な文字列プロパティを追加しようとしましたが、それらをバインドすると、どちらも更新されていません。 IsBusy機能は基本クラスの一部であり、複数のタスクが実行されていない他のビューモデルでも機能するため、XAMLのプロパティ通知やデータバインディングに問題があるとは思われません。
すべてのデータバインディングは、メソッド全体が完了した後に更新されたようです。 UIのスレッドが何らかの形でWaitAllの呼び出しの前にブロックされていると私に信じさせる出力ウィンドウの「最初の例外」またはバインディングエラーは表示されません。
番号2:サービスメソッドから間違ったタスクを返すようです。ビューモデルがすべての結果をコールバック内のすべてのサービスメソッドから変換した後に、WaitAll
の後に実行する必要があります。しかし、サービスメソッドから継続タスクを返すと、継続は呼び出されず、WaitAll
は永遠に待機します。奇妙なことは、ICollectionViewにバインドされたUIコントロールが実際にすべて正しく表示されていることです。これはデータが観測可能なコレクションであり、CollectionViewSourceがコレクションの変更イベントを認識しているためです。
私はWPFコマンドとPrism DelegateCommandで「うまく動作する」かどうかわからないので、async/awaitを使用していません。 Task.ContinueWhenAllは私のために存在しないようですが、私はいくつかのTPL拡張ライブラリを参照する必要がありますか? – BenCr
@BenCrごめんなさい - タスクのファクトリー.ContinueWhenAll。一般的に、待機/非同期のものは、WPFでのタスク継続よりも優れています。主な違いは、あなたが例外を処理するために狂気のフープを飛び越える必要がないということです。 –
ありがとうリード、これはもっとうまくいくようです。うまくいけば、WaitAllが呼び出される前に誰かがブロッキングの説明を与えることができるかもしれないが、これは確かに問題1と2を修正している。私は非同期/待っているものに次の反復で行き、 – BenCr