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> : 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-08-29 07:46:15

MVVM中的RelayCommand与AsyncCommand的相关文章

WPF: 在MVVM中使用Navigtaion

Navigation可以很方便的在页面间进行切换,但是在MVVM模式下,使用Naviation会有一个问题,切换的逻辑需要在ViewModel层完成,但是Navigation需要知道页面的实例或者Uri才能进行切换,那我们如何在ViewModel与UI分离的情况下,用Navigation完成页面的切换呢? 假如有一个程序如下所示,点击Switch之后会从Summary Page切换到另一个页面Detail Page: 在MVVM中,我们需要有三个ViewModel,一个是SummaryViewM

Messenger和MVVM中的View Services

在前面的文章IoC容器和MVVM中, 介绍了IoC容器如何在大量用户类中帮助创建和分配用户类的实例.本文将介绍IoC容器如何帮助应用程序解耦,比如那些根据MVVM模式开发的应用.此模 式广泛应用在基于XAML的应用程序(Silverlignt, WPF, Windows Phone, Windows 8)中,因为此模式与数据绑定系统和用于这类程序设计的工具匹配的很好,尤其是在VS 设计器和Blend中. 在典型的XAML程序中,开发者利用数据绑定系统声明一个XAML UI元素的属性和应用程序中其

在MVVM中使用PasswordBox控件

在MVVM中使用PasswordBox控件,碰到一个问题.由于**PasswordBox.Password**属性并不是一个依赖属性,所以无法将其作为Binding的目标. # 使用附加属性的解决方案 ![Password Demo.gif](http://upload-images.jianshu.io/upload_images/140233-dbd415eb4cf9aeb2.gif) **思路:**定义两个依赖属性**Attach**和**AttachPassoword** Attatch

WPF MVVM中在ViewModel中关闭或者打开Window

这篇博客将介绍在MVVM模式ViewModel中关闭和打开View的方法. 1. ViewModel中关闭View public class MainViewModel { public DelegateCommand<Window> CloseWindowCommand { get; private set; } public MainViewModel() { CloseWindowCommand = new DelegateCommand<Window>(CloseWindo

MVVM中数据验证之 ViewModel vs. Model

                                                  MMVM模式示意图. View绑定到ViewModel,然后执行一些命令在向它请求一个动作.而反过来,ViewModel跟Model通讯,ViewModel告诉Model更新来响应UI. 这样便使得为应用构建UI外观设计相对独立,也非常容易替换UI设计("随心所欲"设计你的界面).同时,当UI和功能越来越松耦合的时候,功能的可测试性就越来越强.   在两个地方验证: 1.在ViewMod

MVVM中写代码的模块区域划分

之前使用MVVM的方式一段时间 + 前天认真的划分设计文档中的模块 = 一种想要强烈的现在使用MVVM的方式 版权声明:本文为博主原创文章,未经博主允许不得转载.

MVVM中的Messenger

通过Mvvm Light源码我们可以知道Messenger的实现细节,如果你现在还不能理解这些代码也没关系,很多东西理解起来远比使用起来难,Messenger也是如此,它使用起来很简单,由于Messenger只公开了一些消息注册和发送方法,使用者一看便知方法的功能,而只需关注要发送的数据和接收的对象就可以了. 1.发送: 1 Messenger.Default.Send<string>("StartRecordVis");//开始录制可见光,发送消息 2.接收: 1 1 M

WPF MVVM 中怎样在ViewModel总打开的对话框在窗体之前

今天在WPF的项目中,写打印插件,在ViewModel中对需要弹出打印对话框,而对话框如果没有Owner所属的时候经常会被当前应用程序遮住,导致我都不知道到底弹出来没有! 参照:http://www.codeproject.com/Articles/20347/WebControls/后,果断顺利通过. 第一步定义WindowWrapper类:实现System.Windows.Forms.IWin32Window接口 1 /// <summary> 2 /// 句柄转换IWin32Window

MVVM中数据结构的更优使用

WPF的Model层用来构建数据结构 对于这些数据结构,我们常规的用法: 一个这个数据结构类型的Collection--->填充数据--->绑定前台控件 好的用法: Model层的数据结构是原料,是其他数据结构的组成部分,我们不直接去用来绑定前台 在ViewModel层建立另外一个数据结构,将Model层的数据结构加入到这里,这个新的数据结构的目的就是用来和前台绑定,你可以将它称为显示类型的数据结构 将这新的数据结构---->填充数据---->绑定前台控件 版权声明:本文为博主原创