使用后来的 .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));
将其分解为可管理的部分,我们将开始一个调用 some method的新任务IEnumerable<Foo> FindData(string)
。这是您一直编写的简单而无聊的同步代码。可能它已经存在于您的视图模型中!
接下来,我们告诉框架在完成使用时启动一项新任务ContinueWith
,但改为在WPF 调度程序上执行。这使您可以避免 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,或者可以将其扩展为封装 aCancellationToken
以支持将来的取消。