Skip to content

Commit fb5c56f

Browse files
authored
Merge pull request #139 from CommunityToolkit/dev/fix-command-no-concurrency-can-execute
Fix async relay command CanExecute raised with no concurrency
2 parents 4f148cf + a3c5527 commit fb5c56f

File tree

4 files changed

+50
-2
lines changed

4 files changed

+50
-2
lines changed

CommunityToolkit.Mvvm/Input/AsyncRelayCommand.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,11 @@ static async void MonitorTask(AsyncRelayCommand @this, Task task)
237237
{
238238
@this.PropertyChanged?.Invoke(@this, CanBeCanceledChangedEventArgs);
239239
}
240+
241+
if (!@this.allowConcurrentExecutions)
242+
{
243+
@this.CanExecuteChanged?.Invoke(@this, EventArgs.Empty);
244+
}
240245
}
241246
}
242247

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,11 @@ static async void MonitorTask(AsyncRelayCommand<T> @this, Task task)
219219
{
220220
@this.PropertyChanged?.Invoke(@this, AsyncRelayCommand.CanBeCanceledChangedEventArgs);
221221
}
222+
223+
if (!@this.allowConcurrentExecutions)
224+
{
225+
@this.CanExecuteChanged?.Invoke(@this, EventArgs.Empty);
226+
}
222227
}
223228
}
224229

tests/CommunityToolkit.Mvvm.UnitTests/Test_AsyncRelayCommand.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,8 @@ public async Task Test_AsyncRelayCommand_AllowConcurrentExecutions_Enable()
207207
Assert.AreSame(args.Item1, command);
208208
Assert.AreSame(args.Item2, EventArgs.Empty);
209209

210+
args = default;
211+
210212
Assert.IsNull(command.ExecutionTask);
211213
Assert.IsFalse(command.IsRunning);
212214

@@ -236,6 +238,10 @@ public async Task Test_AsyncRelayCommand_AllowConcurrentExecutions_Enable()
236238
_ = await Task.WhenAll(cancellationTokenSources[0].Task, cancellationTokenSources[1].Task);
237239

238240
Assert.IsFalse(command.IsRunning);
241+
242+
// CanExecute isn't raised again when the command completes, if concurrent executions are allowed
243+
Assert.IsNull(args.Item1);
244+
Assert.IsNull(args.Item2);
239245
}
240246

241247
[TestMethod]
@@ -275,11 +281,19 @@ private static async Task Test_AsyncRelayCommand_AllowConcurrentExecutions_TestL
275281
Assert.AreSame(args.Item1, command);
276282
Assert.AreSame(args.Item2, EventArgs.Empty);
277283

284+
args = default;
285+
278286
Assert.IsNull(command.ExecutionTask);
279287
Assert.IsFalse(command.IsRunning);
280288

281289
Task task = command.ExecuteAsync(null);
282290

291+
// CanExecute is raised upon execution
292+
Assert.AreSame(args.Item1, command);
293+
Assert.AreSame(args.Item2, EventArgs.Empty);
294+
295+
args = default;
296+
283297
Assert.IsNotNull(command.ExecutionTask);
284298
Assert.AreSame(command.ExecutionTask, task);
285299
Assert.IsTrue(command.IsRunning);
@@ -291,11 +305,19 @@ private static async Task Test_AsyncRelayCommand_AllowConcurrentExecutions_TestL
291305
Assert.IsFalse(command.CanBeCanceled);
292306
Assert.IsFalse(command.IsCancellationRequested);
293307

308+
// CanExecute hasn't been raised again
309+
Assert.IsNull(args.Item1);
310+
Assert.IsNull(args.Item2);
311+
294312
tcs.SetResult(null);
295313

296314
await task;
297315

298316
Assert.IsFalse(command.IsRunning);
317+
318+
// CanExecute is raised automatically when command execution completes, if concurrent executions are disabled
319+
Assert.AreSame(args.Item1, command);
320+
Assert.AreSame(args.Item2, EventArgs.Empty);
299321
}
300322

301323
[TestMethod]

tests/CommunityToolkit.Mvvm.UnitTests/Test_AsyncRelayCommand{T}.cs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ async void TestCallback(Action throwAction, Action completeAction)
267267
Assert.IsTrue(success);
268268
}
269269

270-
public void Test_AsyncRelayCommand_ExecuteDoesNotRaiseCanExecuteChanged()
270+
public async Task Test_AsyncRelayCommand_ExecuteDoesNotRaiseCanExecuteChanged()
271271
{
272272
TaskCompletionSource<object?> tcs = new();
273273

@@ -284,13 +284,21 @@ public void Test_AsyncRelayCommand_ExecuteDoesNotRaiseCanExecuteChanged()
284284
Assert.IsNull(args.Sender);
285285
Assert.IsNull(args.Args);
286286

287+
args = default;
288+
287289
Assert.IsTrue(command.CanExecute(""));
288290

289291
tcs.SetResult(null);
292+
293+
_ = await tcs.Task;
294+
295+
// CanExecute isn't raised when the command completes
296+
Assert.IsNull(args.Sender);
297+
Assert.IsNull(args.Args);
290298
}
291299

292300
[TestMethod]
293-
public void Test_AsyncRelayCommand_ExecuteWithoutConcurrencyRaisesCanExecuteChanged()
301+
public async Task Test_AsyncRelayCommand_ExecuteWithoutConcurrencyRaisesCanExecuteChanged()
294302
{
295303
TaskCompletionSource<object?> tcs = new();
296304

@@ -309,7 +317,15 @@ public void Test_AsyncRelayCommand_ExecuteWithoutConcurrencyRaisesCanExecuteChan
309317

310318
Assert.IsFalse(command.CanExecute(""));
311319

320+
args = default;
321+
312322
tcs.SetResult(null);
323+
324+
_ = await tcs.Task;
325+
326+
// CanExecute is raised again when the command completes
327+
Assert.AreSame(command, args.Sender);
328+
Assert.AreSame(EventArgs.Empty, args.Args);
313329
}
314330

315331
[TestMethod]

0 commit comments

Comments
 (0)