之前发过一个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(); } }
CommandBase
- 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); } }
RelayCommand<T>
其中的泛型是用于接收传给Command的参数的,当然有更多的时候我们的命令不需要任何参数,因此实现一个非泛型的RelayCommand。
public class RelayCommand : RelayCommand<object> { public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null) : base(execute, canExecute) { } }
RelayCommand
- 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(); } }
CancelAsyncCommand
然后实现一个NotifyTaskCompletion,该类用于通知AsyncCommand的完成。
public class NotifyTaskCompletion<TResult> : BindableBase { #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); } private async Task WatchTaskAsync(Task task) { try { await task; } catch { // ignored } if (!IsPropertyChanged) return; OnPropertyChanged(() => Status); OnPropertyChanged(() => IsCompleted); OnPropertyChanged(() => IsNotCompleted); if (task.IsCanceled) { OnPropertyChanged(() => IsCanceled); } else if (task.IsFaulted) { OnPropertyChanged(() => IsFaulted); OnPropertyChanged(() => Exception); OnPropertyChanged(() => InnerException); OnPropertyChanged(() => ErrorMessage); } else { OnPropertyChanged(() => IsSuccessfullyCompleted); OnPropertyChanged(() => Result); } } }
NotifyTaskCompletion
NotifyTaskCompletion的基类BindableBase是实现了INotifyPropertyChanged的类,一般用来作为MVVM模式中ViewModel的基类。
public abstract class BindableBase : INotifyPropertyChanged { protected bool IsPropertyChanged => PropertyChanged != null; public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged([CallerMemberName]string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } protected virtual bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null) { if (Equals(storage, value)) return false; storage = value; // ReSharper disable once ExplicitCallerInfoArgument OnPropertyChanged(propertyName); return true; } protected virtual void OnPropertyChanged<T>(Expression<Func<T>> propertyExpression) { var propertyName = PropertySupport.ExtractPropertyName(propertyExpression); // ReSharper disable once ExplicitCallerInfoArgument OnPropertyChanged(propertyName); } }
BindableBase
在BindableBase中使用了PropertySupport类,这个类是用来从表达式中获取属性名的。
public static class PropertySupport { public static string ExtractPropertyName<T>(Expression<Func<T>> propertyExpression) { if (propertyExpression == null) throw new ArgumentNullException(nameof(propertyExpression)); return ExtractPropertyNameFromLambda(propertyExpression); } internal static string ExtractPropertyNameFromLambda<T>(Expression<Func<T>> expression) { if (expression == null) throw new ArgumentNullException(nameof(expression)); var memberExpression = expression.Body as MemberExpression; if (memberExpression == null) throw new ArgumentNullException(nameof(memberExpression)); var property = memberExpression.Member as PropertyInfo; if (property == null) throw new ArgumentNullException(nameof(property)); var getMethod = property.GetMethod; if (getMethod.IsStatic) throw new ArgumentException(nameof(getMethod)); return property.Name; } }
PropertySupport
然后就是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
最后加个静态类用于创建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); } }
AsyncCommand
引用:异步命令, Prism
时间: 2024-11-07 16:20:00