以前發過一個AsyncCommand實現的文章,該命令用於MVVM中的異步操做。異步
實際上在在MVVM模式中,RelayCommand可能更加經常使用。async
因爲兩種命令均實現ICommand接口,所以咱們將共通的部分提取出來做爲抽象基類CommandBase。ide
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(); } }
實現泛型的RelayCommandthis
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。spa
public class RelayCommand : RelayCommand<object> { public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null) : base(execute, canExecute) { } }
首先實現一個CancelAsyncCommand,用於取消異步命令的執行。code
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的完成。blog
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的實現了!token
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); } }
引用:異步命令, Prismrem