zoukankan      html  css  js  c++  java
  • MVVM中的RelayCommand与AsyncCommand

    之前发过一个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> : 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));
                }
            }
        }
    NotifyTaskCompletion

    然后就是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

  • 相关阅读:
    Linux 创建sftp用户并限制目录权限
    idea操作maven时控制台中文显示乱码/maven项目启动方式
    Docker 容器镜像删除
    Golang 在 Mac、Linux、Windows 下如何交叉编译
    Linux后台运行Jar方法
    MAC安装JDK及环境变量配置
    Docker 容器镜像删除
    UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-2: ordinal not in range(128)
    《设计模式之禅》之状态模式
    《设计模式之禅》之访问者模式
  • 原文地址:https://www.cnblogs.com/zhuyc110/p/5418136.html
Copyright © 2011-2022 走看看