-
-
Save myd7349/c0bab416bdfbb3dc32b275d1918ed3e1 to your computer and use it in GitHub Desktop.
Implementation of AsyncCommand for WPF
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #region Usings | |
| using System; | |
| using System.Reflection; | |
| using System.Threading.Tasks; | |
| using System.Windows.Input; | |
| #endregion | |
| namespace AsyncCommandGist | |
| { | |
| /// <summary> | |
| /// Interface for asynchronous commands that allow to await the command's execution. | |
| /// </summary> | |
| public interface IAsyncCommand : ICommand | |
| { | |
| /// <summary> | |
| /// Executes the task asynchronously. | |
| /// </summary> | |
| /// <returns></returns> | |
| Task ExecuteAsync(object parameter = null); | |
| } | |
| /// <summary> | |
| /// A command that executes an asynchronous delegate. | |
| /// </summary> | |
| public class AsyncCommand : IAsyncCommand | |
| { | |
| private readonly WeakFunc<bool> _canExecute; | |
| /// <summary> | |
| /// The asynchronous delegate to execute. | |
| /// </summary> | |
| private readonly WeakFunc<Task> _commandTask; | |
| /// <summary> | |
| /// Creates a command that executes the specified asynchronous delegate. | |
| /// </summary> | |
| /// <param name="commandTask">The asynchronous delegate to execute. May not be <c>null</c>.</param> | |
| /// <param name="canExecute">The function that returns bool indicating if the command can or cannot execute.</param> | |
| public AsyncCommand(Func<Task> commandTask, Func<bool> canExecute = null) | |
| { | |
| if (commandTask == null) throw new ArgumentNullException(nameof(commandTask)); | |
| _commandTask = WeakFunc<Task>.Create(null, commandTask); | |
| if (canExecute != null) _canExecute = WeakFunc<bool>.Create(null, canExecute); | |
| } | |
| #region IAsyncCommand implementation | |
| /// <summary> | |
| /// Returns a value indicating whether the command may be executed. This implementation always returns <c>true</c>. | |
| /// </summary> | |
| /// <param name="parameter">The parameter for this command.</param> | |
| public virtual bool CanExecute(object parameter = null) | |
| { | |
| return _canExecute?.Execute() ?? true; | |
| } | |
| async void ICommand.Execute(object parameter) | |
| { | |
| await ExecuteAsync(parameter); | |
| } | |
| /// <summary> | |
| /// Executes the command asynchronously. The parameter if passed is always ignored. | |
| /// </summary> | |
| /// <param name="parameter">The parameter is always ignored.</param> | |
| public Task ExecuteAsync(object parameter = null) | |
| { | |
| return _commandTask.Execute(); | |
| } | |
| /// <summary> | |
| /// An event indicating that the return value of <see cref="CanExecute"/> may have changed. | |
| /// </summary> | |
| public event EventHandler CanExecuteChanged | |
| { | |
| add { CommandManager.RequerySuggested += value; } | |
| remove { CommandManager.RequerySuggested -= value; } | |
| } | |
| #endregion | |
| } | |
| /// <summary> | |
| /// Class that stores a <see cref="Func{TResult}"/> without causing a hard reference, so the target can be garbage collected. | |
| /// </summary> | |
| /// <typeparam name="T">Type of the result of the func.</typeparam> | |
| public abstract class WeakFunc<T> | |
| { | |
| /// <summary> | |
| /// Gets or sets the owner of the reference, which might be different from actual target of the func in case of anonymous delegate eg. | |
| /// </summary> | |
| /// <value> | |
| /// The owner reference. | |
| /// </value> | |
| protected WeakReference OwnerReference { get; set; } | |
| /// <summary> | |
| /// Gets a value indicating whether the <see cref="Target"/> is still alive and was not garbage collected. | |
| /// </summary> | |
| public abstract bool IsAlive { get; } | |
| /// <summary> | |
| /// Gets the target of the func or null if it was already garbage collected. | |
| /// </summary> | |
| public object Target => OwnerReference?.Target; | |
| protected WeakFunc(object target) | |
| { | |
| if (target == null) return; | |
| OwnerReference = new WeakReference(target); | |
| } | |
| /// <summary> | |
| /// Executes the actual func | |
| /// </summary> | |
| /// <returns></returns> | |
| public abstract T Execute(); | |
| /// <summary> | |
| /// Creates a specific implementation of WeakFunc. | |
| /// </summary> | |
| /// <typeparam name="TResult">The type of the result.</typeparam> | |
| /// <param name="target">The target.</param> | |
| /// <param name="func">The function.</param> | |
| /// <returns></returns> | |
| public static WeakFunc<TResult> Create<TResult>(object target, Func<TResult> func) | |
| { | |
| if (func.Method.IsStatic) return new WeakStaticFunc<TResult>(target, func); | |
| return new WeakInstanceFunc<TResult>(target, func); | |
| } | |
| #region WeakInstanceFunc | |
| /// <summary> | |
| /// Implementation of a weak func that is target an actual instance (eg. is not static) | |
| /// </summary> | |
| /// <typeparam name="T1"></typeparam> | |
| private class WeakInstanceFunc<T1> : WeakFunc<T1> | |
| { | |
| /// <summary> | |
| /// The actual weak reference to a target of the func, which might be different from the actual target in case of anonymous lambda for example. | |
| /// </summary> | |
| /// <value> | |
| /// The function reference. | |
| /// </value> | |
| private WeakReference FuncReference { get; } | |
| /// <summary> | |
| /// Gets or sets the <see cref="T:System.Reflection.MethodInfo"/> corresponding to this fun's method passed in the constructor. | |
| /// </summary> | |
| private MethodInfo Method { get; } | |
| public override bool IsAlive => OwnerReference?.IsAlive ?? false; | |
| public WeakInstanceFunc(object target, Func<T1> func) : base(target) | |
| { | |
| if (func == null) throw new ArgumentNullException(nameof(func)); | |
| if (func.Method.IsStatic) | |
| throw new ArgumentException( | |
| "The func cannot be static, this implementation of weak func requires instace method."); | |
| Method = func.Method; | |
| FuncReference = new WeakReference(func.Target); | |
| } | |
| public override T1 Execute() | |
| { | |
| var funcTarget = FuncReference.Target; | |
| if (funcTarget == null) return default(T1); | |
| return (T1)Method.Invoke(funcTarget, null); | |
| } | |
| } | |
| #endregion | |
| #region WeakStaticFunc | |
| /// <summary> | |
| /// Special case when the func passed in is static, in this case the func is weak by definition, because | |
| /// it doesn't hold reference to anything and is always alive. The only weak reference is the Target. | |
| /// </summary> | |
| /// <typeparam name="T2"></typeparam> | |
| private class WeakStaticFunc<T2> : WeakFunc<T2> | |
| { | |
| private readonly Func<T2> _staticFunc; | |
| public override bool IsAlive => true; | |
| internal WeakStaticFunc(object target, Func<T2> func) : base(target) | |
| { | |
| if (func == null) throw new ArgumentNullException(nameof(func)); | |
| if (!func.Method.IsStatic) | |
| throw new ArgumentException("The func must be static to create weak static func."); | |
| _staticFunc = func; | |
| } | |
| public override T2 Execute() | |
| { | |
| return _staticFunc(); | |
| } | |
| } | |
| #endregion | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment