码迷,mamicode.com
首页 > 其他好文 > 详细

MVVM中的RelayCommand与AsyncCommand

时间:2016-04-25 14:52:40      阅读:321      评论:0      收藏:0      [点我收藏+]

标签:

之前发过一个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

MVVM中的RelayCommand与AsyncCommand

标签:

原文地址:http://www.cnblogs.com/zhuyc110/p/5418136.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!