-
-
Notifications
You must be signed in to change notification settings - Fork 136
/
AsyncCommand.cs
139 lines (121 loc) · 4.75 KB
/
AsyncCommand.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
using MvvmHelpers.Interfaces;
using System;
using System.Threading.Tasks;
using System.Windows.Input;
// Lovely code from our good friend John Tririet
// https://johnthiriet.com/mvvm-going-async-with-async-command
namespace MvvmHelpers.Commands
{
/// <summary>
/// Implementation of an Async Command
/// </summary>
public class AsyncCommand : IAsyncCommand
{
readonly Func<Task> execute;
readonly Func<object, bool>? canExecute;
readonly Action<Exception>? onException;
readonly bool continueOnCapturedContext;
readonly WeakEventManager weakEventManager = new WeakEventManager();
/// <summary>
/// Create a new AsyncCommand
/// </summary>
/// <param name="execute">Function to execute</param>
/// <param name="canExecute">Function to call to determine if it can be executed</param>
/// <param name="onException">Action callback when an exception occurs</param>
/// <param name="continueOnCapturedContext">If the context should be captured on exception</param>
public AsyncCommand(Func<Task> execute,
Func<object, bool>? canExecute = null,
Action<Exception>? onException = null,
bool continueOnCapturedContext = false)
{
this.execute = execute ?? throw new ArgumentNullException(nameof(execute));
this.canExecute = canExecute;
this.onException = onException;
this.continueOnCapturedContext = continueOnCapturedContext;
}
/// <summary>
/// Event triggered when Can Excecute changes.
/// </summary>
public event EventHandler CanExecuteChanged
{
add { weakEventManager.AddEventHandler(value); }
remove { weakEventManager.RemoveEventHandler(value); }
}
/// <summary>
/// Invoke the CanExecute method and return if it can be executed.
/// </summary>
/// <param name="parameter">Parameter to pass to CanExecute.</param>
/// <returns>If it can be executed.</returns>
public bool CanExecute(object parameter) => canExecute?.Invoke(parameter) ?? true;
/// <summary>
/// Execute the command async.
/// </summary>
/// <returns>Task of action being executed that can be awaited.</returns>
public Task ExecuteAsync() => execute();
/// <summary>
/// Raise a CanExecute change event.
/// </summary>
public void RaiseCanExecuteChanged() => weakEventManager.HandleEvent(this, EventArgs.Empty, nameof(CanExecuteChanged));
#region Explicit implementations
void ICommand.Execute(object parameter) => ExecuteAsync().SafeFireAndForget(onException, continueOnCapturedContext);
#endregion
}
/// <summary>
/// Implementation of a generic Async Command
/// </summary>
public class AsyncCommand<T> : IAsyncCommand<T>
{
readonly Func<T, Task> execute;
readonly Func<object, bool>? canExecute;
readonly Action<Exception>? onException;
readonly bool continueOnCapturedContext;
readonly WeakEventManager weakEventManager = new WeakEventManager();
/// <summary>
/// Create a new AsyncCommand
/// </summary>
/// <param name="execute">Function to execute</param>
/// <param name="canExecute">Function to call to determine if it can be executed</param>
/// <param name="onException">Action callback when an exception occurs</param>
/// <param name="continueOnCapturedContext">If the context should be captured on exception</param>
public AsyncCommand(Func<T, Task> execute,
Func<object, bool>? canExecute = null,
Action<Exception>? onException = null,
bool continueOnCapturedContext = false)
{
this.execute = execute ?? throw new ArgumentNullException(nameof(execute));
this.canExecute = canExecute;
this.onException = onException;
this.continueOnCapturedContext = continueOnCapturedContext;
}
/// <summary>
/// Event triggered when Can Excecute changes.
/// </summary>
public event EventHandler CanExecuteChanged
{
add { weakEventManager.AddEventHandler(value); }
remove { weakEventManager.RemoveEventHandler(value); }
}
/// <summary>
/// Invoke the CanExecute method and return if it can be executed.
/// </summary>
/// <param name="parameter">Parameter to pass to CanExecute.</param>
/// <returns>If it can be executed</returns>
public bool CanExecute(object parameter) => canExecute?.Invoke(parameter) ?? true;
/// <summary>
/// Execute the command async.
/// </summary>
/// <returns>Task that is executing and can be awaited.</returns>
public Task ExecuteAsync(T parameter) => execute(parameter);
/// <summary>
/// Raise a CanExecute change event.
/// </summary>
public void RaiseCanExecuteChanged() => weakEventManager.HandleEvent(this, EventArgs.Empty, nameof(CanExecuteChanged));
#region Explicit implementations
void ICommand.Execute(object parameter)
{
if (CommandUtils.IsValidCommandParameter<T>(parameter))
ExecuteAsync((T)parameter).SafeFireAndForget(onException, continueOnCapturedContext);
}
#endregion
}
}