提交 973bf1f0 编写于 作者: D Dan Siegel

chore: add support for providing a default CancellationToken factory

上级 d0e3be3e
...@@ -17,6 +17,7 @@ public class AsyncDelegateCommand : DelegateCommandBase, IAsyncCommand ...@@ -17,6 +17,7 @@ public class AsyncDelegateCommand : DelegateCommandBase, IAsyncCommand
private bool _isExecuting = false; private bool _isExecuting = false;
private readonly Func<CancellationToken, Task> _executeMethod; private readonly Func<CancellationToken, Task> _executeMethod;
private Func<bool> _canExecuteMethod; private Func<bool> _canExecuteMethod;
private Func<CancellationToken> _getCancellationToken = () => CancellationToken.None;
/// <summary> /// <summary>
/// Creates a new instance of <see cref="AsyncDelegateCommand"/> with the <see cref="Func{Task}"/> to invoke on execution. /// Creates a new instance of <see cref="AsyncDelegateCommand"/> with the <see cref="Func{Task}"/> to invoke on execution.
...@@ -131,7 +132,7 @@ public class AsyncDelegateCommand : DelegateCommandBase, IAsyncCommand ...@@ -131,7 +132,7 @@ public class AsyncDelegateCommand : DelegateCommandBase, IAsyncCommand
/// <param name="parameter">Command Parameter</param> /// <param name="parameter">Command Parameter</param>
protected override async void Execute(object parameter) protected override async void Execute(object parameter)
{ {
await Execute(); await Execute(_getCancellationToken());
} }
/// <summary> /// <summary>
...@@ -147,19 +148,30 @@ public class AsyncDelegateCommand : DelegateCommandBase, IAsyncCommand ...@@ -147,19 +148,30 @@ public class AsyncDelegateCommand : DelegateCommandBase, IAsyncCommand
/// <summary> /// <summary>
/// Enables Parallel Execution of Async Tasks /// Enables Parallel Execution of Async Tasks
/// </summary> /// </summary>
/// <returns></returns> /// <returns>The current instance of <see cref="AsyncDelegateCommand"/>.</returns>
public AsyncDelegateCommand EnableParallelExecution() public AsyncDelegateCommand EnableParallelExecution()
{ {
_enableParallelExecution = true; _enableParallelExecution = true;
return this; return this;
} }
/// <summary>
/// Provides a delegate callback to provide a default CancellationToken when the Command is invoked.
/// </summary>
/// <param name="factory">The default <see cref="CancellationToken"/> Factory.</param>
/// <returns>The current instance of <see cref="AsyncDelegateCommand"/>.</returns>
public AsyncDelegateCommand CancellationTokenSourceFactory(Func<CancellationToken> factory)
{
_getCancellationToken = factory;
return this;
}
/// <summary> /// <summary>
/// Observes a property that implements INotifyPropertyChanged, and automatically calls DelegateCommandBase.RaiseCanExecuteChanged on property changed notifications. /// Observes a property that implements INotifyPropertyChanged, and automatically calls DelegateCommandBase.RaiseCanExecuteChanged on property changed notifications.
/// </summary> /// </summary>
/// <typeparam name="T">The object type containing the property specified in the expression.</typeparam> /// <typeparam name="T">The object type containing the property specified in the expression.</typeparam>
/// <param name="propertyExpression">The property expression. Example: ObservesProperty(() => PropertyName).</param> /// <param name="propertyExpression">The property expression. Example: ObservesProperty(() => PropertyName).</param>
/// <returns>The current instance of DelegateCommand</returns> /// <returns>The current instance of <see cref="AsyncDelegateCommand"/>.</returns>
public AsyncDelegateCommand ObservesProperty<T>(Expression<Func<T>> propertyExpression) public AsyncDelegateCommand ObservesProperty<T>(Expression<Func<T>> propertyExpression)
{ {
ObservesPropertyInternal(propertyExpression); ObservesPropertyInternal(propertyExpression);
...@@ -170,7 +182,7 @@ public class AsyncDelegateCommand : DelegateCommandBase, IAsyncCommand ...@@ -170,7 +182,7 @@ public class AsyncDelegateCommand : DelegateCommandBase, IAsyncCommand
/// Observes a property that is used to determine if this command can execute, and if it implements INotifyPropertyChanged it will automatically call DelegateCommandBase.RaiseCanExecuteChanged on property changed notifications. /// Observes a property that is used to determine if this command can execute, and if it implements INotifyPropertyChanged it will automatically call DelegateCommandBase.RaiseCanExecuteChanged on property changed notifications.
/// </summary> /// </summary>
/// <param name="canExecuteExpression">The property expression. Example: ObservesCanExecute(() => PropertyName).</param> /// <param name="canExecuteExpression">The property expression. Example: ObservesCanExecute(() => PropertyName).</param>
/// <returns>The current instance of DelegateCommand</returns> /// <returns>The current instance of <see cref="AsyncDelegateCommand"/>.</returns>
public AsyncDelegateCommand ObservesCanExecute(Expression<Func<bool>> canExecuteExpression) public AsyncDelegateCommand ObservesCanExecute(Expression<Func<bool>> canExecuteExpression)
{ {
_canExecuteMethod = canExecuteExpression.Compile(); _canExecuteMethod = canExecuteExpression.Compile();
...@@ -182,7 +194,7 @@ public class AsyncDelegateCommand : DelegateCommandBase, IAsyncCommand ...@@ -182,7 +194,7 @@ public class AsyncDelegateCommand : DelegateCommandBase, IAsyncCommand
/// Provides the ability to connect a delegate to catch exceptions encountered by CanExecute or the Execute methods of the DelegateCommand /// Provides the ability to connect a delegate to catch exceptions encountered by CanExecute or the Execute methods of the DelegateCommand
/// </summary> /// </summary>
/// <param name="catch">TThe callback when a specific exception is encountered</param> /// <param name="catch">TThe callback when a specific exception is encountered</param>
/// <returns>The current instance of DelegateCommand</returns> /// <returns>The current instance of <see cref="AsyncDelegateCommand"/>.</returns>
public AsyncDelegateCommand Catch<TException>(Action<TException> @catch) public AsyncDelegateCommand Catch<TException>(Action<TException> @catch)
where TException : Exception where TException : Exception
{ {
...@@ -194,7 +206,7 @@ public class AsyncDelegateCommand : DelegateCommandBase, IAsyncCommand ...@@ -194,7 +206,7 @@ public class AsyncDelegateCommand : DelegateCommandBase, IAsyncCommand
/// Provides the ability to connect a delegate to catch exceptions encountered by CanExecute or the Execute methods of the DelegateCommand /// Provides the ability to connect a delegate to catch exceptions encountered by CanExecute or the Execute methods of the DelegateCommand
/// </summary> /// </summary>
/// <param name="catch">The generic / default callback when an exception is encountered</param> /// <param name="catch">The generic / default callback when an exception is encountered</param>
/// <returns>The current instance of DelegateCommand</returns> /// <returns>The current instance of <see cref="AsyncDelegateCommand"/>.</returns>
public AsyncDelegateCommand Catch(Action<Exception> @catch) public AsyncDelegateCommand Catch(Action<Exception> @catch)
{ {
ExceptionHandler.Register<Exception>(@catch); ExceptionHandler.Register<Exception>(@catch);
...@@ -203,7 +215,7 @@ public class AsyncDelegateCommand : DelegateCommandBase, IAsyncCommand ...@@ -203,7 +215,7 @@ public class AsyncDelegateCommand : DelegateCommandBase, IAsyncCommand
Task IAsyncCommand.ExecuteAsync(object? parameter) Task IAsyncCommand.ExecuteAsync(object? parameter)
{ {
return Execute(default); return Execute(_getCancellationToken());
} }
Task IAsyncCommand.ExecuteAsync(object? parameter, CancellationToken cancellationToken) Task IAsyncCommand.ExecuteAsync(object? parameter, CancellationToken cancellationToken)
......
...@@ -18,6 +18,7 @@ public class AsyncDelegateCommand<T> : DelegateCommandBase, IAsyncCommand ...@@ -18,6 +18,7 @@ public class AsyncDelegateCommand<T> : DelegateCommandBase, IAsyncCommand
private bool _isExecuting = false; private bool _isExecuting = false;
private readonly Func<T, CancellationToken, Task> _executeMethod; private readonly Func<T, CancellationToken, Task> _executeMethod;
private Func<T, bool> _canExecuteMethod; private Func<T, bool> _canExecuteMethod;
private Func<CancellationToken> _getCancellationToken = () => CancellationToken.None;
/// <summary> /// <summary>
/// Creates a new instance of <see cref="AsyncDelegateCommand{T}"/> with the <see cref="Func{Task}"/> to invoke on execution. /// Creates a new instance of <see cref="AsyncDelegateCommand{T}"/> with the <see cref="Func{Task}"/> to invoke on execution.
...@@ -135,7 +136,7 @@ public class AsyncDelegateCommand<T> : DelegateCommandBase, IAsyncCommand ...@@ -135,7 +136,7 @@ public class AsyncDelegateCommand<T> : DelegateCommandBase, IAsyncCommand
{ {
try try
{ {
await Execute((T)parameter); await Execute((T)parameter, _getCancellationToken());
} }
catch (Exception ex) catch (Exception ex)
{ {
...@@ -171,19 +172,30 @@ public class AsyncDelegateCommand<T> : DelegateCommandBase, IAsyncCommand ...@@ -171,19 +172,30 @@ public class AsyncDelegateCommand<T> : DelegateCommandBase, IAsyncCommand
/// <summary> /// <summary>
/// Enables Parallel Execution of Async Tasks /// Enables Parallel Execution of Async Tasks
/// </summary> /// </summary>
/// <returns></returns> /// <returns>The current instance of <see cref="AsyncDelegateCommand{T}"/>.</returns>
public AsyncDelegateCommand<T> EnableParallelExecution() public AsyncDelegateCommand<T> EnableParallelExecution()
{ {
_enableParallelExecution = true; _enableParallelExecution = true;
return this; return this;
} }
/// <summary>
/// Provides a delegate callback to provide a default CancellationToken when the Command is invoked.
/// </summary>
/// <param name="factory">The default <see cref="CancellationToken"/> Factory.</param>
/// <returns>The current instance of <see cref="AsyncDelegateCommand{T}"/>.</returns>
public AsyncDelegateCommand<T> CancellationTokenSourceFactory(Func<CancellationToken> factory)
{
_getCancellationToken = factory;
return this;
}
/// <summary> /// <summary>
/// Observes a property that implements INotifyPropertyChanged, and automatically calls DelegateCommandBase.RaiseCanExecuteChanged on property changed notifications. /// Observes a property that implements INotifyPropertyChanged, and automatically calls DelegateCommandBase.RaiseCanExecuteChanged on property changed notifications.
/// </summary> /// </summary>
/// <typeparam name="TType">The type of the return value of the method that this delegate encapsulates</typeparam> /// <typeparam name="TType">The type of the return value of the method that this delegate encapsulates</typeparam>
/// <param name="propertyExpression">The property expression. Example: ObservesProperty(() => PropertyName).</param> /// <param name="propertyExpression">The property expression. Example: ObservesProperty(() => PropertyName).</param>
/// <returns>The current instance of DelegateCommand</returns> /// <returns>The current instance of <see cref="AsyncDelegateCommand{T}"/>.</returns>
public AsyncDelegateCommand<T> ObservesProperty<TType>(Expression<Func<TType>> propertyExpression) public AsyncDelegateCommand<T> ObservesProperty<TType>(Expression<Func<TType>> propertyExpression)
{ {
ObservesPropertyInternal(propertyExpression); ObservesPropertyInternal(propertyExpression);
...@@ -194,7 +206,7 @@ public class AsyncDelegateCommand<T> : DelegateCommandBase, IAsyncCommand ...@@ -194,7 +206,7 @@ public class AsyncDelegateCommand<T> : DelegateCommandBase, IAsyncCommand
/// Observes a property that is used to determine if this command can execute, and if it implements INotifyPropertyChanged it will automatically call DelegateCommandBase.RaiseCanExecuteChanged on property changed notifications. /// Observes a property that is used to determine if this command can execute, and if it implements INotifyPropertyChanged it will automatically call DelegateCommandBase.RaiseCanExecuteChanged on property changed notifications.
/// </summary> /// </summary>
/// <param name="canExecuteExpression">The property expression. Example: ObservesCanExecute(() => PropertyName).</param> /// <param name="canExecuteExpression">The property expression. Example: ObservesCanExecute(() => PropertyName).</param>
/// <returns>The current instance of DelegateCommand</returns> /// <returns>The current instance of <see cref="AsyncDelegateCommand{T}"/>.</returns>
public AsyncDelegateCommand<T> ObservesCanExecute(Expression<Func<bool>> canExecuteExpression) public AsyncDelegateCommand<T> ObservesCanExecute(Expression<Func<bool>> canExecuteExpression)
{ {
Expression<Func<T, bool>> expression = Expression.Lambda<Func<T, bool>>(canExecuteExpression.Body, Expression.Parameter(typeof(T), "o")); Expression<Func<T, bool>> expression = Expression.Lambda<Func<T, bool>>(canExecuteExpression.Body, Expression.Parameter(typeof(T), "o"));
...@@ -207,7 +219,7 @@ public class AsyncDelegateCommand<T> : DelegateCommandBase, IAsyncCommand ...@@ -207,7 +219,7 @@ public class AsyncDelegateCommand<T> : DelegateCommandBase, IAsyncCommand
/// Provides the ability to connect a delegate to catch exceptions encountered by CanExecute or the Execute methods of the DelegateCommand /// Provides the ability to connect a delegate to catch exceptions encountered by CanExecute or the Execute methods of the DelegateCommand
/// </summary> /// </summary>
/// <param name="catch">TThe callback when a specific exception is encountered</param> /// <param name="catch">TThe callback when a specific exception is encountered</param>
/// <returns>The current instance of DelegateCommand</returns> /// <returns>The current instance of <see cref="AsyncDelegateCommand{T}"/>.</returns>
public AsyncDelegateCommand<T> Catch<TException>(Action<TException> @catch) public AsyncDelegateCommand<T> Catch<TException>(Action<TException> @catch)
where TException : Exception where TException : Exception
{ {
...@@ -219,7 +231,7 @@ public class AsyncDelegateCommand<T> : DelegateCommandBase, IAsyncCommand ...@@ -219,7 +231,7 @@ public class AsyncDelegateCommand<T> : DelegateCommandBase, IAsyncCommand
/// Provides the ability to connect a delegate to catch exceptions encountered by CanExecute or the Execute methods of the DelegateCommand /// Provides the ability to connect a delegate to catch exceptions encountered by CanExecute or the Execute methods of the DelegateCommand
/// </summary> /// </summary>
/// <param name="catch">The generic / default callback when an exception is encountered</param> /// <param name="catch">The generic / default callback when an exception is encountered</param>
/// <returns>The current instance of DelegateCommand</returns> /// <returns>The current instance of <see cref="AsyncDelegateCommand{T}"/>.</returns>
public AsyncDelegateCommand<T> Catch(Action<Exception> @catch) public AsyncDelegateCommand<T> Catch(Action<Exception> @catch)
{ {
ExceptionHandler.Register<Exception>(@catch); ExceptionHandler.Register<Exception>(@catch);
...@@ -231,7 +243,7 @@ public class AsyncDelegateCommand<T> : DelegateCommandBase, IAsyncCommand ...@@ -231,7 +243,7 @@ public class AsyncDelegateCommand<T> : DelegateCommandBase, IAsyncCommand
try try
{ {
// If T is not nullable this may throw an exception // If T is not nullable this may throw an exception
await Execute((T)parameter, default); await Execute((T)parameter, _getCancellationToken());
} }
catch (Exception ex) catch (Exception ex)
{ {
......
using System.Threading.Tasks; using System.Threading;
using System.Threading; using System.Threading.Tasks;
using System.Windows.Input;
using Prism.Commands; using Prism.Commands;
using Xunit; using Xunit;
...@@ -116,4 +117,19 @@ public class AsyncDelegateCommandFixture ...@@ -116,4 +117,19 @@ public class AsyncDelegateCommandFixture
Assert.True(executionStarted); Assert.True(executionStarted);
Assert.False(executed); Assert.False(executed);
} }
[Fact]
public async Task ICommandExecute_UsesDefaultTokenSourceFactory()
{
var cts = new CancellationTokenSource();
var command = new AsyncDelegateCommand((token) => Task.Delay(1000, token))
.CancellationTokenSourceFactory(() => cts.Token);
ICommand iCommand = command;
iCommand.Execute(null);
Assert.True(command.IsExecuting);
cts.Cancel();
Assert.False(command.IsExecuting);
}
} }
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册