【WPF】AsyncCommand的实现

WPF的ICommand里,使用异步命令,且可获得异步结果。

【接口示例】

MVVM的示例,在ViewModel里定义Command,回调方法里随便异步插入点数据,返回object类型。

// ViewModel代码片段:
public ICommand InsertCommand { get; set; }

public MainViewModel()
{
    InsertCommand = new AsyncCommand<object>(InsertCallback);
}

private async Task<object> InsertCallback(CancellationToken arg)
{
    //339
    var tempData = new List<string[]>();
    for (int i = 0; i < 100; i++)
    {
        tempData.Add(new string[] { "God", "9999", "hello,world." });
    }
    Stopwatch myWatch = new Stopwatch();
    myWatch.Start();
    await SqliteDepository.Instance.InsertValueRange("mytable", tempData);
    myWatch.Stop();
    Debug.WriteLine($"{ myWatch.ElapsedMilliseconds }ms");

    //7951
    await Task.Run(async () =>
    {
        myWatch.Reset();
        myWatch.Start();
        foreach (var item in tempData)
        {
            await SqliteDepository.Instance.InsertValues("mytable", item);
        }
        myWatch.Stop();
        Debug.WriteLine($"{ myWatch.ElapsedMilliseconds }ms");
    });
           
    return null;
}

View里定义按钮绑定命令。

<Button
    Width="100"
    Height="30"
    Margin="3"
    Command="{Binding InsertCommand}"
    Content="Insert" />

【具体实现】

1. AsyncCommand和AsyncCommand<TResult>类:

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);
    }
}

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));
        OnPropertyChanged();
        await Execution.TaskCompletion;
        _cancelCommand.NotifyCommandFinished();
        OnPropertyChanged();
    }

    protected override async void OnExecute(object parameter)
    {
        await ExecuteAsync(parameter);
    }
}

2.CommandBase类,继承于ICommand

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();
	}
}

3. 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();
	}
}

4.NotifyTaskCompletion<TResult>类

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));
        }
    }
}

参考自:.Net5 WPF编程进阶教程(完结)_哔哩哔哩_bilibili

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值