Skip to content

Commit 79204ab

Browse files
committed
Switch async command types to use AsyncRelayCommandOptions
1 parent 6d73083 commit 79204ab

File tree

6 files changed

+62
-58
lines changed

6 files changed

+62
-58
lines changed

CommunityToolkit.Mvvm.SourceGenerators/Input/RelayCommandGenerator.Execute.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,11 @@ public static ImmutableArray<MemberDeclarationSyntax> GetSyntax(CommandInfo comm
207207
// Enable concurrent executions, if requested
208208
if (commandInfo.AllowConcurrentExecutions)
209209
{
210-
commandCreationArguments.Add(Argument(LiteralExpression(SyntaxKind.TrueLiteralExpression)));
210+
commandCreationArguments.Add(
211+
Argument(MemberAccessExpression(
212+
SyntaxKind.SimpleMemberAccessExpression,
213+
IdentifierName("global::CommunityToolkit.Mvvm.Input.AsyncRelayCommandOptions"),
214+
IdentifierName("AllowConcurrentExecutions"))));
211215
}
212216

213217
// Construct the generated property as follows (the explicit delegate cast is needed to avoid overload resolution conflicts):

CommunityToolkit.Mvvm/Input/AsyncRelayCommand.cs

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,9 @@ public sealed class AsyncRelayCommand : IAsyncRelayCommand, ICancellationAwareCo
5959
private readonly Func<bool>? canExecute;
6060

6161
/// <summary>
62-
/// Indicates whether or not concurrent executions of the command are allowed.
62+
/// The options being set for the current command.
6363
/// </summary>
64-
private readonly bool allowConcurrentExecutions;
64+
private readonly AsyncRelayCommandOptions options;
6565

6666
/// <summary>
6767
/// The <see cref="CancellationTokenSource"/> instance to use to cancel <see cref="cancelableExecute"/>.
@@ -91,14 +91,14 @@ public AsyncRelayCommand(Func<Task> execute)
9191
/// Initializes a new instance of the <see cref="AsyncRelayCommand"/> class.
9292
/// </summary>
9393
/// <param name="execute">The execution logic.</param>
94-
/// <param name="allowConcurrentExecutions">Whether or not to allow concurrent executions of the command.</param>
94+
/// <param name="options">The options to use to configure the async command.</param>
9595
/// <exception cref="System.ArgumentNullException">Thrown if <paramref name="execute"/> is <see langword="null"/>.</exception>
96-
public AsyncRelayCommand(Func<Task> execute, bool allowConcurrentExecutions)
96+
public AsyncRelayCommand(Func<Task> execute, AsyncRelayCommandOptions options)
9797
{
9898
ArgumentNullException.ThrowIfNull(execute);
9999

100100
this.execute = execute;
101-
this.allowConcurrentExecutions = allowConcurrentExecutions;
101+
this.options = options;
102102
}
103103

104104
/// <summary>
@@ -117,14 +117,14 @@ public AsyncRelayCommand(Func<CancellationToken, Task> cancelableExecute)
117117
/// Initializes a new instance of the <see cref="AsyncRelayCommand"/> class.
118118
/// </summary>
119119
/// <param name="cancelableExecute">The cancelable execution logic.</param>
120-
/// <param name="allowConcurrentExecutions">Whether or not to allow concurrent executions of the command.</param>
120+
/// <param name="options">The options to use to configure the async command.</param>
121121
/// <exception cref="System.ArgumentNullException">Thrown if <paramref name="cancelableExecute"/> is <see langword="null"/>.</exception>
122-
public AsyncRelayCommand(Func<CancellationToken, Task> cancelableExecute, bool allowConcurrentExecutions)
122+
public AsyncRelayCommand(Func<CancellationToken, Task> cancelableExecute, AsyncRelayCommandOptions options)
123123
{
124124
ArgumentNullException.ThrowIfNull(cancelableExecute);
125125

126126
this.cancelableExecute = cancelableExecute;
127-
this.allowConcurrentExecutions = allowConcurrentExecutions;
127+
this.options = options;
128128
}
129129

130130
/// <summary>
@@ -147,16 +147,16 @@ public AsyncRelayCommand(Func<Task> execute, Func<bool> canExecute)
147147
/// </summary>
148148
/// <param name="execute">The execution logic.</param>
149149
/// <param name="canExecute">The execution status logic.</param>
150-
/// <param name="allowConcurrentExecutions">Whether or not to allow concurrent executions of the command.</param>
150+
/// <param name="options">The options to use to configure the async command.</param>
151151
/// <exception cref="System.ArgumentNullException">Thrown if <paramref name="execute"/> or <paramref name="canExecute"/> are <see langword="null"/>.</exception>
152-
public AsyncRelayCommand(Func<Task> execute, Func<bool> canExecute, bool allowConcurrentExecutions)
152+
public AsyncRelayCommand(Func<Task> execute, Func<bool> canExecute, AsyncRelayCommandOptions options)
153153
{
154154
ArgumentNullException.ThrowIfNull(execute);
155155
ArgumentNullException.ThrowIfNull(canExecute);
156156

157157
this.execute = execute;
158158
this.canExecute = canExecute;
159-
this.allowConcurrentExecutions = allowConcurrentExecutions;
159+
this.options = options;
160160
}
161161

162162
/// <summary>
@@ -179,16 +179,16 @@ public AsyncRelayCommand(Func<CancellationToken, Task> cancelableExecute, Func<b
179179
/// </summary>
180180
/// <param name="cancelableExecute">The cancelable execution logic.</param>
181181
/// <param name="canExecute">The execution status logic.</param>
182-
/// <param name="allowConcurrentExecutions">Whether or not to allow concurrent executions of the command.</param>
182+
/// <param name="options">The options to use to configure the async command.</param>
183183
/// <exception cref="System.ArgumentNullException">Thrown if <paramref name="cancelableExecute"/> or <paramref name="canExecute"/> are <see langword="null"/>.</exception>
184-
public AsyncRelayCommand(Func<CancellationToken, Task> cancelableExecute, Func<bool> canExecute, bool allowConcurrentExecutions)
184+
public AsyncRelayCommand(Func<CancellationToken, Task> cancelableExecute, Func<bool> canExecute, AsyncRelayCommandOptions options)
185185
{
186186
ArgumentNullException.ThrowIfNull(cancelableExecute);
187187
ArgumentNullException.ThrowIfNull(canExecute);
188188

189189
this.cancelableExecute = cancelableExecute;
190190
this.canExecute = canExecute;
191-
this.allowConcurrentExecutions = allowConcurrentExecutions;
191+
this.options = options;
192192
}
193193

194194
private Task? executionTask;
@@ -238,7 +238,7 @@ static async void MonitorTask(AsyncRelayCommand @this, Task task)
238238
@this.PropertyChanged?.Invoke(@this, CanBeCanceledChangedEventArgs);
239239
}
240240

241-
if (!@this.allowConcurrentExecutions)
241+
if ((@this.options & AsyncRelayCommandOptions.AllowConcurrentExecutions) == 0)
242242
{
243243
@this.CanExecuteChanged?.Invoke(@this, EventArgs.Empty);
244244
}
@@ -273,7 +273,7 @@ public bool CanExecute(object? parameter)
273273
{
274274
bool canExecute = this.canExecute?.Invoke() != false;
275275

276-
return canExecute && (this.allowConcurrentExecutions || ExecutionTask is not { IsCompleted: false });
276+
return canExecute && ((this.options & AsyncRelayCommandOptions.AllowConcurrentExecutions) != 0 || ExecutionTask is not { IsCompleted: false });
277277
}
278278

279279
/// <inheritdoc/>
@@ -304,7 +304,7 @@ public Task ExecuteAsync(object? parameter)
304304
}
305305

306306
// If concurrent executions are disabled, notify the can execute change as well
307-
if (!this.allowConcurrentExecutions)
307+
if ((this.options & AsyncRelayCommandOptions.AllowConcurrentExecutions) == 0)
308308
{
309309
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
310310
}

CommunityToolkit.Mvvm/Input/AsyncRelayCommand{T}.cs

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,9 @@ public sealed class AsyncRelayCommand<T> : IAsyncRelayCommand<T>, ICancellationA
3636
private readonly Predicate<T?>? canExecute;
3737

3838
/// <summary>
39-
/// Indicates whether or not concurrent executions of the command are allowed.
39+
/// The options being set for the current command.
4040
/// </summary>
41-
private readonly bool allowConcurrentExecutions;
41+
private readonly AsyncRelayCommandOptions options;
4242

4343
/// <summary>
4444
/// The <see cref="CancellationTokenSource"/> instance to use to cancel <see cref="cancelableExecute"/>.
@@ -68,15 +68,15 @@ public AsyncRelayCommand(Func<T?, Task> execute)
6868
/// Initializes a new instance of the <see cref="AsyncRelayCommand{T}"/> class.
6969
/// </summary>
7070
/// <param name="execute">The execution logic.</param>
71-
/// <param name="allowConcurrentExecutions">Whether or not to allow concurrent executions of the command.</param>
71+
/// <param name="options">The options to use to configure the async command.</param>
7272
/// <remarks>See notes in <see cref="RelayCommand{T}(Action{T})"/>.</remarks>
7373
/// <exception cref="System.ArgumentNullException">Thrown if <paramref name="execute"/> is <see langword="null"/>.</exception>
74-
public AsyncRelayCommand(Func<T?, Task> execute, bool allowConcurrentExecutions)
74+
public AsyncRelayCommand(Func<T?, Task> execute, AsyncRelayCommandOptions options)
7575
{
7676
ArgumentNullException.ThrowIfNull(execute);
7777

7878
this.execute = execute;
79-
this.allowConcurrentExecutions = allowConcurrentExecutions;
79+
this.options = options;
8080
}
8181

8282
/// <summary>
@@ -96,15 +96,15 @@ public AsyncRelayCommand(Func<T?, CancellationToken, Task> cancelableExecute)
9696
/// Initializes a new instance of the <see cref="AsyncRelayCommand{T}"/> class.
9797
/// </summary>
9898
/// <param name="cancelableExecute">The cancelable execution logic.</param>
99-
/// <param name="allowConcurrentExecutions">Whether or not to allow concurrent executions of the command.</param>
99+
/// <param name="options">The options to use to configure the async command.</param>
100100
/// <remarks>See notes in <see cref="RelayCommand{T}(Action{T})"/>.</remarks>
101101
/// <exception cref="System.ArgumentNullException">Thrown if <paramref name="cancelableExecute"/> is <see langword="null"/>.</exception>
102-
public AsyncRelayCommand(Func<T?, CancellationToken, Task> cancelableExecute, bool allowConcurrentExecutions)
102+
public AsyncRelayCommand(Func<T?, CancellationToken, Task> cancelableExecute, AsyncRelayCommandOptions options)
103103
{
104104
ArgumentNullException.ThrowIfNull(cancelableExecute);
105105

106106
this.cancelableExecute = cancelableExecute;
107-
this.allowConcurrentExecutions = allowConcurrentExecutions;
107+
this.options = options;
108108
}
109109

110110
/// <summary>
@@ -128,17 +128,17 @@ public AsyncRelayCommand(Func<T?, Task> execute, Predicate<T?> canExecute)
128128
/// </summary>
129129
/// <param name="execute">The execution logic.</param>
130130
/// <param name="canExecute">The execution status logic.</param>
131-
/// <param name="allowConcurrentExecutions">Whether or not to allow concurrent executions of the command.</param>
131+
/// <param name="options">The options to use to configure the async command.</param>
132132
/// <remarks>See notes in <see cref="RelayCommand{T}(Action{T})"/>.</remarks>
133133
/// <exception cref="System.ArgumentNullException">Thrown if <paramref name="execute"/> or <paramref name="canExecute"/> are <see langword="null"/>.</exception>
134-
public AsyncRelayCommand(Func<T?, Task> execute, Predicate<T?> canExecute, bool allowConcurrentExecutions)
134+
public AsyncRelayCommand(Func<T?, Task> execute, Predicate<T?> canExecute, AsyncRelayCommandOptions options)
135135
{
136136
ArgumentNullException.ThrowIfNull(execute);
137137
ArgumentNullException.ThrowIfNull(canExecute);
138138

139139
this.execute = execute;
140140
this.canExecute = canExecute;
141-
this.allowConcurrentExecutions = allowConcurrentExecutions;
141+
this.options = options;
142142
}
143143

144144
/// <summary>
@@ -162,17 +162,17 @@ public AsyncRelayCommand(Func<T?, CancellationToken, Task> cancelableExecute, Pr
162162
/// </summary>
163163
/// <param name="cancelableExecute">The cancelable execution logic.</param>
164164
/// <param name="canExecute">The execution status logic.</param>
165-
/// <param name="allowConcurrentExecutions">Whether or not to allow concurrent executions of the command.</param>
165+
/// <param name="options">The options to use to configure the async command.</param>
166166
/// <remarks>See notes in <see cref="RelayCommand{T}(Action{T})"/>.</remarks>
167167
/// <exception cref="System.ArgumentNullException">Thrown if <paramref name="cancelableExecute"/> or <paramref name="canExecute"/> are <see langword="null"/>.</exception>
168-
public AsyncRelayCommand(Func<T?, CancellationToken, Task> cancelableExecute, Predicate<T?> canExecute, bool allowConcurrentExecutions)
168+
public AsyncRelayCommand(Func<T?, CancellationToken, Task> cancelableExecute, Predicate<T?> canExecute, AsyncRelayCommandOptions options)
169169
{
170170
ArgumentNullException.ThrowIfNull(cancelableExecute);
171171
ArgumentNullException.ThrowIfNull(canExecute);
172172

173173
this.cancelableExecute = cancelableExecute;
174174
this.canExecute = canExecute;
175-
this.allowConcurrentExecutions = allowConcurrentExecutions;
175+
this.options = options;
176176
}
177177

178178
private Task? executionTask;
@@ -220,7 +220,7 @@ static async void MonitorTask(AsyncRelayCommand<T> @this, Task task)
220220
@this.PropertyChanged?.Invoke(@this, AsyncRelayCommand.CanBeCanceledChangedEventArgs);
221221
}
222222

223-
if (!@this.allowConcurrentExecutions)
223+
if ((@this.options & AsyncRelayCommandOptions.AllowConcurrentExecutions) == 0)
224224
{
225225
@this.CanExecuteChanged?.Invoke(@this, EventArgs.Empty);
226226
}
@@ -255,7 +255,7 @@ public bool CanExecute(T? parameter)
255255
{
256256
bool canExecute = this.canExecute?.Invoke(parameter) != false;
257257

258-
return canExecute && (this.allowConcurrentExecutions || ExecutionTask is not { IsCompleted: false });
258+
return canExecute && ((this.options & AsyncRelayCommandOptions.AllowConcurrentExecutions) != 0 || ExecutionTask is not { IsCompleted: false });
259259
}
260260

261261
/// <inheritdoc/>
@@ -306,7 +306,7 @@ public Task ExecuteAsync(T? parameter)
306306
}
307307

308308
// If concurrent executions are disabled, notify the can execute change as well
309-
if (!this.allowConcurrentExecutions)
309+
if ((this.options & AsyncRelayCommandOptions.AllowConcurrentExecutions) == 0)
310310
{
311311
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
312312
}

tests/CommunityToolkit.Mvvm.UnitTests/Test_ArgumentNullException.Input.cs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -30,33 +30,33 @@ public void Test_ArgumentNullException_RelayCommandOfT()
3030
public void Test_ArgumentNullException_AsyncRelayCommand()
3131
{
3232
Assert(() => new AsyncRelayCommand(execute: null!), "execute");
33-
Assert(() => new AsyncRelayCommand(execute: null!, true), "execute");
33+
Assert(() => new AsyncRelayCommand(execute: null!, AsyncRelayCommandOptions.AllowConcurrentExecutions), "execute");
3434
Assert(() => new AsyncRelayCommand(cancelableExecute: null!), "cancelableExecute");
35-
Assert(() => new AsyncRelayCommand(cancelableExecute: null!, true), "cancelableExecute");
35+
Assert(() => new AsyncRelayCommand(cancelableExecute: null!, AsyncRelayCommandOptions.AllowConcurrentExecutions), "cancelableExecute");
3636
Assert(() => new AsyncRelayCommand(execute: null!, () => true), "execute");
3737
Assert(() => new AsyncRelayCommand(() => Task.CompletedTask, canExecute: null!), "canExecute");
38-
Assert(() => new AsyncRelayCommand(execute: null!, () => true, true), "execute");
39-
Assert(() => new AsyncRelayCommand(() => Task.CompletedTask, canExecute: null!, true), "canExecute");
38+
Assert(() => new AsyncRelayCommand(execute: null!, () => true, AsyncRelayCommandOptions.AllowConcurrentExecutions), "execute");
39+
Assert(() => new AsyncRelayCommand(() => Task.CompletedTask, canExecute: null!, AsyncRelayCommandOptions.AllowConcurrentExecutions), "canExecute");
4040
Assert(() => new AsyncRelayCommand(cancelableExecute: null!, () => true), "cancelableExecute");
4141
Assert(() => new AsyncRelayCommand(t => Task.CompletedTask, canExecute: null!), "canExecute");
42-
Assert(() => new AsyncRelayCommand(cancelableExecute: null!, () => true, true), "cancelableExecute");
43-
Assert(() => new AsyncRelayCommand(t => Task.CompletedTask, canExecute: null!, true), "canExecute");
42+
Assert(() => new AsyncRelayCommand(cancelableExecute: null!, () => true, AsyncRelayCommandOptions.AllowConcurrentExecutions), "cancelableExecute");
43+
Assert(() => new AsyncRelayCommand(t => Task.CompletedTask, canExecute: null!, AsyncRelayCommandOptions.AllowConcurrentExecutions), "canExecute");
4444
}
4545

4646
[TestMethod]
4747
public void Test_ArgumentNullException_AsyncRelayCommandOfT()
4848
{
4949
Assert(() => new AsyncRelayCommand<string>(execute: null!), "execute");
50-
Assert(() => new AsyncRelayCommand<string>(execute: null!, true), "execute");
50+
Assert(() => new AsyncRelayCommand<string>(execute: null!, AsyncRelayCommandOptions.AllowConcurrentExecutions), "execute");
5151
Assert(() => new AsyncRelayCommand<string>(cancelableExecute: null!), "cancelableExecute");
52-
Assert(() => new AsyncRelayCommand<string>(cancelableExecute: null!, true), "cancelableExecute");
52+
Assert(() => new AsyncRelayCommand<string>(cancelableExecute: null!, AsyncRelayCommandOptions.AllowConcurrentExecutions), "cancelableExecute");
5353
Assert(() => new AsyncRelayCommand<string>(execute: null!, s => true), "execute");
5454
Assert(() => new AsyncRelayCommand<string>(s => Task.CompletedTask, canExecute: null!), "canExecute");
55-
Assert(() => new AsyncRelayCommand<string>(execute: null!, s => true, true), "execute");
56-
Assert(() => new AsyncRelayCommand<string>(s => Task.CompletedTask, canExecute: null!, true), "canExecute");
55+
Assert(() => new AsyncRelayCommand<string>(execute: null!, s => true, AsyncRelayCommandOptions.AllowConcurrentExecutions), "execute");
56+
Assert(() => new AsyncRelayCommand<string>(s => Task.CompletedTask, canExecute: null!, AsyncRelayCommandOptions.AllowConcurrentExecutions), "canExecute");
5757
Assert(() => new AsyncRelayCommand<string>(cancelableExecute: null!, s => true), "cancelableExecute");
5858
Assert(() => new AsyncRelayCommand<string>(t => Task.CompletedTask, canExecute: null!), "canExecute");
59-
Assert(() => new AsyncRelayCommand<string>(cancelableExecute: null!, s => true, true), "cancelableExecute");
60-
Assert(() => new AsyncRelayCommand<string>(t => Task.CompletedTask, canExecute: null!, true), "canExecute");
59+
Assert(() => new AsyncRelayCommand<string>(cancelableExecute: null!, s => true, AsyncRelayCommandOptions.AllowConcurrentExecutions), "cancelableExecute");
60+
Assert(() => new AsyncRelayCommand<string>(t => Task.CompletedTask, canExecute: null!, AsyncRelayCommandOptions.AllowConcurrentExecutions), "canExecute");
6161
}
6262
}

0 commit comments

Comments
 (0)