後の.NetFramework(4.0以降)とWPFを使用すると、System.Threading.Tasks
ライブラリを利用して、内部でこの作業の多くを提供できます。
コマンドでビューモデルのプロパティを更新する必要があるが、情報を待つ必要がある場合は、タスクを開始してIOを実行するだけです。
this.FindDataCommand = new RelayCommand<string>(
/* ICommand.Execute */
value =>
{
Task.Factory
.StartNew<IEnumerable<Foo>>(() => FindData(value))
.ContinueWith(
task =>
{
this.foundData.Clear();
this.foundData.AddRange(task.Result);
},
TaskScheduler.FromCurrentSynchronizationContext());
},
/* ICommand.CanExecute */
value => !String.IsNullOrWhitespace(value));
これを管理可能な部分に分解して、いくつかのメソッドを呼び出す新しいタスクを開始してIEnumerable<Foo> FindData(string)
います。これは、あなたがいつも書いてきた昔ながらの退屈な同期コードです。おそらくそれはあなたのビューモデルにすでに存在しています!
次に、フレームワークに、の使用が終了したときに新しいタスクを開始するように指示しますが、代わりにWPFディスパッチャーで実行します。これにより、UI要素に関するクロススレッドの問題の煩わしさを回避できます。ContinueWith
ヘルパークラスを使用して監視するためにこれを拡張できます。
public class TaskManager
{
private static ConcurrentDictionary<Dispatcher, TaskManager> _map
= new ConcurrentDictionary<Dispatcher, TaskManager>();
public ObservableCollection<WorkItem> Running
{
get;
private set;
}
public TaskManager()
{
this.Running = new ObservableCollection<WorkItem>();
}
public static TaskManager Get(Dispatcher dispatcher)
{
return _map.GetOrAdd(dispatcher, new TaskManager());
}
// ...
XAMLでこのクラスを使用すると、そのインスタンスをウィンドウに追加することになりますViewModel
。
public TaskManager CurrentTaskManager
{
get { return TaskManager.Get(Dispatcher.CurrentDispatcher); }
}
// <StatusBarItem Content="{Binding CurrentTaskManager.Running.Count}" />
次に、タスクマネージャーにメソッドを追加して、Runningコレクションとの間のタスクの追加を処理します。
public Task<TResult> StartNew<TResult>(Func<TResult> work)
{
var task = Task.Factory
.StartNew<TResult>(work);
// build our view model
var workItem = new WorkItem(task);
this.Running.Add(workItem);
// Pass the result back using ContinueWith
return task.ContinueWith(
t => { this.Running.Remove(workItem); return t.Result; },
TaskScheduler.FromCurrentSynchronizationContext());
}
ここで、FindDataCommand
実装を変更するだけです。
TaskManager.Get(Dispatcher.CurrentDispatcher)
.StartNew<IEnumerable<Foo>>(() => FindData(value))
.ContinueWith(
task =>
{
this.foundData.Clear();
this.foundData.AddRange(task.Result);
},
TaskScheduler.FromCurrentSynchronizationContext());
クラスは、クラスWorkItem
のプロパティをUIに公開することも、将来のキャンセルをサポートするためTask
にをカプセル化するように拡張することもできます。CancellationToken