之前发过一个AsyncCommand实现的文章,该命令用于MVVM中的异步操作。
实际上在在MVVM模式中,RelayCommand可能更加常用。
由于两种命令均实现ICommand接口,因此我们将共通的部分提取出来作为抽象基类CommandBase。
public abstract class CommandBase : ICommand { public event EventHandler CanExecuteChanged { add { CommandManager.RequerySuggested += value; } remove { CommandManager.RequerySuggested -= value; } } public virtual bool CanExecute(object parameter) { return true; } public void Execute(object parameter) { if (CanExecute(parameter) == false) return; OnExecute(parameter); } protected abstract void OnExecute(object parameter); protected void RaiseCanExecuteChanged() { CommandManager.InvalidateRequerySuggested(); } }
- RelayCommand实现
实现泛型的RelayCommand
public class RelayCommand<T> : CommandBase { private readonly Action<T> _execute; private readonly Func<T, bool> _canExecute; public RelayCommand(Action<T> execute, Func<T, bool> canExecute = null) { if (execute == null) throw new ArgumentNullException(nameof(execute)); if (canExecute == null) canExecute = _ => true; _execute = execute; _canExecute = canExecute; } public override bool CanExecute(object parameter) { return _canExecute((T)parameter); } protected override void OnExecute(object parameter) { _execute((T)parameter); } }
其中的泛型是用于接收传给Command的参数的,当然有更多的时候我们的命令不需要任何参数,因此实现一个非泛型的RelayCommand。
public class RelayCommand : RelayCommand<object> { public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null) : base(execute, canExecute) { } }
- AsyncCommand实现
首先实现一个CancelAsyncCommand,用于取消异步命令的执行。
class CancelAsyncCommand : CommandBase { private CancellationTokenSource _cts = new CancellationTokenSource(); private bool _commandExecuting; public CancellationToken Token => _cts.Token; public void NotifyCommandStarting() { _commandExecuting = true; if (!_cts.IsCancellationRequested) return; _cts = new CancellationTokenSource(); RaiseCanExecuteChanged(); } public void NotifyCommandFinished() { _commandExecuting = false; RaiseCanExecuteChanged(); } public override bool CanExecute(object parameter) { return _commandExecuting && !_cts.IsCancellationRequested; } protected override void OnExecute(object parameter) { _cts.Cancel(); RaiseCanExecuteChanged(); } }
接着实现一个NotifyTaskCompletion,该类用于通知AsyncCommand的完成。
public class NotifyTaskCompletion<TResult> : INotifyPropertyChanged { #region property public Task<TResult> Task { get; private set; } public Task TaskCompletion { get; private set; } public TResult Result => (Task.Status == TaskStatus.RanToCompletion) ? Task.Result : default(TResult); public TaskStatus Status => Task.Status; public bool IsCompleted => Task.IsCompleted; public bool IsNotCompleted => !Task.IsCompleted; public bool IsSuccessfullyCompleted => Task.Status == TaskStatus.RanToCompletion; public bool IsCanceled => Task.IsCanceled; public bool IsFaulted => Task.IsFaulted; public AggregateException Exception => Task.Exception; public Exception InnerException => Exception?.InnerException; public string ErrorMessage => InnerException?.InnerException.Message; #endregion public NotifyTaskCompletion(Task<TResult> task) { Task = task; TaskCompletion = WatchTaskAsync(task); } public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } private async Task WatchTaskAsync(Task task) { try { await task; } catch { // ignored } finally { NotifyPropertiesChanged(task); } } private void NotifyPropertiesChanged(Task task) { var handle = PropertyChanged; if(handle == null) { return; } OnPropertyChanged(nameof(Status)); OnPropertyChanged(nameof(IsCompleted)); OnPropertyChanged(nameof(IsNotCompleted)); if (task.IsCanceled) { OnPropertyChanged(nameof(IsCanceled)); } else if (task.IsFaulted) { OnPropertyChanged(nameof(IsFaulted)); OnPropertyChanged(nameof(Exception)); OnPropertyChanged(nameof(InnerException)); OnPropertyChanged(nameof(ErrorMessage)); } else { OnPropertyChanged(nameof(IsSuccessfullyCompleted)); OnPropertyChanged(nameof(Result)); } } }
然后就是AsyncCommand的实现了!
public class AsyncCommand<TResult> : CommandBase, INotifyPropertyChanged { #region fields private readonly Func<CancellationToken, Task<TResult>> _command; private readonly CancelAsyncCommand _cancelCommand; private NotifyTaskCompletion<TResult> _execution; #endregion #region properties public ICommand CancelCommand => _cancelCommand; public NotifyTaskCompletion<TResult> Execution { get { return _execution; } private set { _execution = value; OnPropertyChanged(); } } #endregion public AsyncCommand(Func<CancellationToken, Task<TResult>> command) { _command = command; _cancelCommand = new CancelAsyncCommand(); } public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } public override bool CanExecute(object parameter) { return Execution == null || Execution.IsCompleted; } public async Task ExecuteAsync(object parameter) { _cancelCommand.NotifyCommandStarting(); Execution = new NotifyTaskCompletion<TResult>(_command(_cancelCommand.Token)); RaiseCanExecuteChanged(); await Execution.TaskCompletion; _cancelCommand.NotifyCommandFinished(); RaiseCanExecuteChanged(); } protected override async void OnExecute(object parameter) { await ExecuteAsync(parameter); } }
最后加个静态类用于创建AsyncCommand。
public static class AsyncCommand { public static AsyncCommand<object> Create(Func<Task> command) { return new AsyncCommand<object>(async _ => { await command(); return null; }); } public static AsyncCommand<TResult> Create<TResult>(Func<Task<TResult>> command) { return new AsyncCommand<TResult>(_ => command()); } public static AsyncCommand<object> Create(Func<CancellationToken, Task> command) { return new AsyncCommand<object>(async token => { await command(token); return null; }); } public static AsyncCommand<TResult> Create<TResult>(Func<CancellationToken, Task<TResult>> command) { return new AsyncCommand<TResult>(command); } }
引用:异步命令, Prism