异步WPF命令

注意 :如果您需要完整的源代码,则此问题中的代码是deSleeper的一部分。

我想要的命令之一是异步操作的烘焙设计。 我希望在执行命令时按下按钮以禁用,并在完成时返回。 我希望在ThreadPool工作项中执行实际工作。 最后,我想要一种方法来处理异步处理过程中发生的任何错误。

我的解决方案是AsyncCommand:

public abstract class AsyncCommand : ICommand { public event EventHandler CanExecuteChanged; public event EventHandler ExecutionStarting; public event EventHandler ExecutionComplete; public abstract string Text { get; } private bool _isExecuting; public bool IsExecuting { get { return _isExecuting; } private set { _isExecuting = value; if (CanExecuteChanged != null) CanExecuteChanged(this, EventArgs.Empty); } } protected abstract void OnExecute(object parameter); public void Execute(object parameter) { try { IsExecuting = true; if (ExecutionStarting != null) ExecutionStarting(this, EventArgs.Empty); var dispatcher = Dispatcher.CurrentDispatcher; ThreadPool.QueueUserWorkItem( obj => { try { OnExecute(parameter); if (ExecutionComplete != null) dispatcher.Invoke(DispatcherPriority.Normal, ExecutionComplete, this, new AsyncCommandCompleteEventArgs(null)); } catch (Exception ex) { if (ExecutionComplete != null) dispatcher.Invoke(DispatcherPriority.Normal, ExecutionComplete, this, new AsyncCommandCompleteEventArgs(ex)); } finally { dispatcher.Invoke(DispatcherPriority.Normal, new Action(() => IsExecuting = false)); } }); } catch (Exception ex) { IsExecuting = false; if (ExecutionComplete != null) ExecutionComplete(this, new AsyncCommandCompleteEventArgs(ex)); } } public virtual bool CanExecute(object parameter) { return !IsExecuting; } } 

所以问题是:这一切都是必要的吗? 我注意到内置异步支持数据绑定,为什么不执行命令? 也许它与参数问题有关,这是我的下一个问题。

我已经能够优化原始样本,并为遇到类似情况的其他人提供一些建议。

首先,考虑BackgroundWorker是否满足需求。 我仍然经常使用AsyncCommand来获得自动禁用function,但是如果使用BackgroundWorker可以做很多事情。

但是通过包装BackgroundWorker,AsyncCommand提供了具有异步行为的命令function(我还有关于此主题的博客条目 )

 public abstract class AsyncCommand : ICommand { public event EventHandler CanExecuteChanged; public event EventHandler RunWorkerStarting; public event RunWorkerCompletedEventHandler RunWorkerCompleted; public abstract string Text { get; } private bool _isExecuting; public bool IsExecuting { get { return _isExecuting; } private set { _isExecuting = value; if (CanExecuteChanged != null) CanExecuteChanged(this, EventArgs.Empty); } } protected abstract void OnExecute(object parameter); public void Execute(object parameter) { try { onRunWorkerStarting(); var worker = new BackgroundWorker(); worker.DoWork += ((sender, e) => OnExecute(e.Argument)); worker.RunWorkerCompleted += ((sender, e) => onRunWorkerCompleted(e)); worker.RunWorkerAsync(parameter); } catch (Exception ex) { onRunWorkerCompleted(new RunWorkerCompletedEventArgs(null, ex, true)); } } private void onRunWorkerStarting() { IsExecuting = true; if (RunWorkerStarting != null) RunWorkerStarting(this, EventArgs.Empty); } private void onRunWorkerCompleted(RunWorkerCompletedEventArgs e) { IsExecuting = false; if (RunWorkerCompleted != null) RunWorkerCompleted(this, e); } public virtual bool CanExecute(object parameter) { return !IsExecuting; } } 

正如我在你的另一个问题中回答的那样,你可能仍然希望同步绑定到它,然后异步启动命令。 这样你就可以避免现在遇到的问题。