中继命令可以执行和任务

我想在调用relay命令时启动任务,但是只要该任务正在运行,我想禁用该按钮

举个例子

private ICommand update; public ICommand Update { get { if (update == null) { update = new RelayCommand( param => Task.Factory.StartNew(()=> StartUpdate()), param => true); //true means the button will always be enabled } return update; } } 

检查该任务是否正在运行的最佳方法是什么?

这是我的解决方案,但不确定它是否是最佳方式

 class Vm : ObservableObject { Task T; public Vm() { T = new Task(() => doStuff()); } private ICommand myCommand; public ICommand MyCommand { get { return myCommand ?? (myCommand = new RelayCommand( p => { T = new Task(() => doStuff()); T.Start(); }, p => T.Status != TaskStatus.Running)); } } private void doStuff() { System.Threading.Thread.Sleep(5000); } } 

更新:这里的每个答案都可以正常工作,但他们仍然不同意,我只是达到了100的声誉,每当我达到100时我就会开始获得赏金,所以我要找的是一个实现最佳非内存泄漏的异步在.net 4.0中的任务中执行的RelayCommand

我想,你可以使用AsyncCommand的这个实现。

 public class AsyncCommand : ICommand, IDisposable { private readonly BackgroundWorker _backgroundWorker = new BackgroundWorker {WorkerSupportsCancellation = true}; private readonly Func _canExecute; public AsyncCommand(Action action, Func canExecute = null, Action completed = null, Action error = null) { _backgroundWorker.DoWork += (s, e) => { CommandManager.InvalidateRequerySuggested(); action(); }; _backgroundWorker.RunWorkerCompleted += (s, e) => { if (completed != null && e.Error == null) completed(e.Result); if (error != null && e.Error != null) error(e.Error); CommandManager.InvalidateRequerySuggested(); }; _canExecute = canExecute; } public void Cancel() { if (_backgroundWorker.IsBusy) _backgroundWorker.CancelAsync(); } public bool CanExecute(object parameter) { return _canExecute == null ? !_backgroundWorker.IsBusy : !_backgroundWorker.IsBusy && _canExecute(); } public void Execute(object parameter) { _backgroundWorker.RunWorkerAsync(); } public event EventHandler CanExecuteChanged { add { CommandManager.RequerySuggested += value; } remove { CommandManager.RequerySuggested -= value; } } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (disposing) { if (_backgroundWorker != null) _backgroundWorker.Dispose(); } } } 

我强烈建议您避免使用new Task以及Task.Factory.StartNew 。 在后台线程上启动异步任务的正确方法是Task.Run

您可以使用此模式轻松创建异步RelayCommand

 private bool updateInProgress; private ICommand update; public ICommand Update { get { if (update == null) { update = new RelayCommand( async () => { updateInProgress = true; Update.RaiseCanExecuteChanged(); await Task.Run(() => StartUpdate()); updateInProgress = false; Update.RaiseCanExecuteChanged(); }, () => !updateInProgress); } return update; } } 

因此,使用RelayCommand的解决方案几乎可以正常工作。 问题是在任务完成运行后UI不会立即更新。 这是因为某些东西需要触发ICommand的CanExecuteChanged事件才能使UI正确更新。

解决此问题的一种方法是创建一种新的ICommand。 例如:

  class AsyncRelayCommand : ICommand { private Func _action; private Task _task; public AsyncRelayCommand(Func action) { _action = action; } public bool CanExecute(object parameter) { return _task == null || _task.IsCompleted; } public event EventHandler CanExecuteChanged; public async void Execute(object parameter) { _task = _action(parameter); OnCanExecuteChanged(); await _task; OnCanExecuteChanged(); } private void OnCanExecuteChanged() { var handler = this.CanExecuteChanged; if (handler != null) handler(this, EventArgs.Empty); } } 

现在,您的视图模型可以执行以下操作

 private ICommand myCommand; public ICommand MyCommand { get { return myCommand ?? (myCommand = new AsyncRelayCommand(p => Task.Factory.StartNew(doStuff))); } } private void doStuff() { System.Threading.Thread.Sleep(5000); } 

或者你可以让你的doStuff函数像这样的“异步”函数

 private ICommand myCommand2; public ICommand MyCommand2 { get { return myCommand2 ?? (myCommand2 = new AsyncRelayCommand(p => doStuff2())); } } private async Task doStuff2() { await Task.Delay(5000); } 

您可以拥有一个静态变量IsRunning ,您可以在任务启动时将其设置为True,在完成时设置为false,并将该启用的按钮绑定到IsRunning的状态

我试图避免Prism库从参考组件的安装角度尽可能地保持我的控制简单,我最终得到了这个解决方案

 _cmd = new RelayCommand(async delegate { await Task.Run(() => ()); }, delegate { return !IsInProgress; }) ); 

似乎运作良好。 (如果您不需要传递commandParameter)。 不幸的是,这仍然是个问题。

RelayCommand类inheritance自ICommand

 public class RelayCommand : ICommand { private Action _execute; private Predicate _canExecute; private event EventHandler CanExecuteChangedInternal; public RelayCommand(Action execute) : this(execute, DefaultCanExecute) { } public RelayCommand(Action execute, Predicate canExecute) { if (execute == null) { throw new ArgumentNullException("execute"); } if (canExecute == null) { throw new ArgumentNullException("canExecute"); } _execute = execute; _canExecute = canExecute; } public event EventHandler CanExecuteChanged { add { CommandManager.RequerySuggested += value; CanExecuteChangedInternal += value; } remove { CommandManager.RequerySuggested -= value; CanExecuteChangedInternal -= value; } } public bool CanExecute(object parameter) { return _canExecute != null && _canExecute(parameter); } public void Execute(object parameter) { _execute(parameter); } public void OnCanExecuteChanged() { EventHandler handler = CanExecuteChangedInternal; if (handler != null) { //DispatcherHelper.BeginInvokeOnUIThread(() => handler.Invoke(this, EventArgs.Empty)); handler.Invoke(this, EventArgs.Empty); } } public void Destroy() { _canExecute = _ => false; _execute = _ => { return; }; } private static bool DefaultCanExecute(object parameter) { return true; } }