使用后来的 .Net Frameworks (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));
将其分解为可管理的部分,我们是 starting a new task,它调用了一些方法 IEnumerable<Foo> FindData(string)。这是您一直编写的简单而无聊的同步代码。它可能已经存在于您的视图模型中!
接下来我们使用ContinueWith 告诉框架start a new task when that one finishes,但要在WPF Dispatcher instead 上执行。这可以让您避免 UI 元素的跨线程问题的麻烦。
您可以使用辅助类扩展它以进行监控:
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 中使用此类类似于将其实例添加到 Window 的 ViewModel:
public TaskManager CurrentTaskManager
{
get { return TaskManager.Get(Dispatcher.CurrentDispatcher); }
}
// <StatusBarItem Content="{Binding CurrentTaskManager.Running.Count}" />
然后,您将向 TaskManager 添加一个方法来处理向 Running 集合添加任务和从 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 类可以将Task 类的属性暴露给 UI,或者它可以扩展为封装 CancellationToken 以支持将来取消。