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