• [转]异步command的实现


    在WPF中,有时有些重操作,需要异步完成,来避免Ui锁死。

    MVVM模式下,可以通过异步Command来实现此过程。

    首先定义一个IAsyncCommand ,实现ICommand:

        public interface IAsyncCommand : ICommand
        {
            Task ExecuteAsync(object parameter);
        }

    然后实现一个实现该接口的抽象Base:

        public abstract class AsyncCommandBase : IAsyncCommand
        {
            public abstract bool CanExecute(object parameter);
            public abstract Task ExecuteAsync(object parameter);
    
            public async void Execute(object parameter)
            {
                await ExecuteAsync(parameter);
            }
            public event EventHandler CanExecuteChanged
            {
                add { CommandManager.RequerySuggested += value; }
                remove { CommandManager.RequerySuggested -= value; }
            }
            protected void RaiseCanExecuteChanged()
            {
                CommandManager.InvalidateRequerySuggested();
            }
        }
    AsyncCommandBase

    再实现上述抽象类:

        public class AsyncCommand<TResult> : AsyncCommandBase, INotifyPropertyChanged
        {
            private readonly Func<CancellationToken, Task<TResult>> _command;
            private readonly CancelAsyncCommand _cancelCommand;
            private NotifyTaskCompletion<TResult> _execution;
    
            public AsyncCommand(Func<CancellationToken, Task<TResult>> command)
            {
                _command = command;
                _cancelCommand = new CancelAsyncCommand();
            }
    
            public override bool CanExecute(object parameter)
            {
                return Execution == null || Execution.IsCompleted;
            }
    
            public override async Task ExecuteAsync(object parameter)
            {
                _cancelCommand.NotifyCommandStarting();
                Execution = new NotifyTaskCompletion<TResult>(_command(_cancelCommand.Token));
                RaiseCanExecuteChanged();
                await Execution.TaskCompletion;
                _cancelCommand.NotifyCommandFinished();
                RaiseCanExecuteChanged();
            }
    
            public ICommand CancelCommand
            {
                get { return _cancelCommand; }
            }
    
            public NotifyTaskCompletion<TResult> Execution
            {
                get { return _execution; }
                private set
                {
                    _execution = value;
                    OnPropertyChanged();
                }
            }
    
            public event PropertyChangedEventHandler PropertyChanged;
            protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
            {
                PropertyChangedEventHandler handler = PropertyChanged;
                if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
            }
    
            private sealed class CancelAsyncCommand : ICommand
            {
                private CancellationTokenSource _cts = new CancellationTokenSource();
                private bool _commandExecuting;
    
                public CancellationToken Token { get { return _cts.Token; } }
    
                public void NotifyCommandStarting()
                {
                    _commandExecuting = true;
                    if (!_cts.IsCancellationRequested)
                        return;
                    _cts = new CancellationTokenSource();
                    RaiseCanExecuteChanged();
                }
    
                public void NotifyCommandFinished()
                {
                    _commandExecuting = false;
                    RaiseCanExecuteChanged();
                }
    
                bool ICommand.CanExecute(object parameter)
                {
                    return _commandExecuting && !_cts.IsCancellationRequested;
                }
    
                void ICommand.Execute(object parameter)
                {
                    _cts.Cancel();
                    RaiseCanExecuteChanged();
                }
    
                public event EventHandler CanExecuteChanged
                {
                    add { CommandManager.RequerySuggested += value; }
                    remove { CommandManager.RequerySuggested -= value; }
                }
    
                private void RaiseCanExecuteChanged()
                {
                    CommandManager.InvalidateRequerySuggested();
                }
            }
        }
    
        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

    其中的NotifyTaskCompletion实现了任务结束通知:

        public sealed class NotifyTaskCompletion<TResult> : INotifyPropertyChanged
        {
            public NotifyTaskCompletion(Task<TResult> task)
            {
                Task = task;
                TaskCompletion = WatchTaskAsync(task);
            }
            private async Task WatchTaskAsync(Task task)
            {
                try
                {
                    await task;
                }
                catch
                {
                }
                var propertyChanged = PropertyChanged;
                if (propertyChanged == null)
                    return;
                propertyChanged(this, new PropertyChangedEventArgs("Status"));
                propertyChanged(this, new PropertyChangedEventArgs("IsCompleted"));
                propertyChanged(this, new PropertyChangedEventArgs("IsNotCompleted"));
                if (task.IsCanceled)
                {
                    propertyChanged(this, new PropertyChangedEventArgs("IsCanceled"));
                }
                else if (task.IsFaulted)
                {
                    propertyChanged(this, new PropertyChangedEventArgs("IsFaulted"));
                    propertyChanged(this, new PropertyChangedEventArgs("Exception"));
                    propertyChanged(this, new PropertyChangedEventArgs("InnerException"));
                    propertyChanged(this, new PropertyChangedEventArgs("ErrorMessage"));
                }
                else
                {
                    propertyChanged(this, new PropertyChangedEventArgs("IsSuccessfullyCompleted"));
                    propertyChanged(this, new PropertyChangedEventArgs("Result"));
                }
            }
            public Task<TResult> Task { get; private set; }
            public Task TaskCompletion { get; private set; }
            public TResult Result
            {
                get
                {
                    return (Task.Status == TaskStatus.RanToCompletion) ?
                        Task.Result : default(TResult);
                }
            }
            public TaskStatus Status { get { return Task.Status; } }
            public bool IsCompleted { get { return Task.IsCompleted; } }
            public bool IsNotCompleted { get { return !Task.IsCompleted; } }
            public bool IsSuccessfullyCompleted
            {
                get
                {
                    return Task.Status ==
                        TaskStatus.RanToCompletion;
                }
            }
            public bool IsCanceled { get { return Task.IsCanceled; } }
            public bool IsFaulted { get { return Task.IsFaulted; } }
            public AggregateException Exception { get { return Task.Exception; } }
            public Exception InnerException
            {
                get
                {
                    return (Exception == null) ?
                        null : Exception.InnerException;
                }
            }
            public string ErrorMessage
            {
                get
                {
                    return (InnerException == null) ?
                        null : InnerException.Message;
                }
            }
            public event PropertyChangedEventHandler PropertyChanged;
        }
    NotifyTaskCompletion

    假设为一个Button绑定一个IAsyncCommand,这个Button对应的命令就可以异步执行,而不锁死UI了。

    ※在原文章的代码中NotifyTaskCompletion实现有个bug,下载下来的代码是修复过的。千万别复制文章中的实现~

  • 相关阅读:
    架构,改善程序复用性的设计(目录)
    如何让你的系统配置文件更合理
    MVC验证(自动在基类中验证实体的数据有效性),本人最满意的作品之一
    MVC验证(只验证指定字段)
    Redis学习笔记~实现消息队列比MSMQ更方便
    FRG图像文件格式(四):编码技术
    缓冲区
    Oracle体系结构及备份(十一)——bcakgroundprocess
    Excel编程(2)自动填充
    设计模式:策略模式
  • 原文地址:https://www.cnblogs.com/zhuyc110/p/5210829.html
Copyright © 2020-2023  润新知