using System; using System.Collections.Generic; using System.Linq.Expressions; using System.Reflection; using System.Resources; using System.Text; using System.Threading.Tasks; namespace HandyControl.Interactivity.Commands { /// /// An whose delegates can be attached for and . /// /// Parameter type. /// /// The constructor deliberately prevents the use of value types. /// Because ICommand takes an object, having a value type for T would cause unexpected behavior when CanExecute(null) is called during XAML initialization for command bindings. /// Using default(T) was considered and rejected as a solution because the implementor would not be able to distinguish between a valid and defaulted values. /// /// Instead, callers should support a value type by using a nullable value type and checking the HasValue property before using the Value property. /// /// /// public MyClass() /// { /// this.submitCommand = new DelegateCommand<int?>(this.Submit, this.CanSubmit); /// } /// /// private bool CanSubmit(int? customerId) /// { /// return (customerId.HasValue && customers.Contains(customerId.Value)); /// } /// /// /// public class DelegateCommand : DelegateCommandBase { readonly Action _executeMethod; Func _canExecuteMethod; /// /// Initializes a new instance of . /// /// Delegate to execute when Execute is called on the command. This can be null to just hook up a CanExecute delegate. /// will always return true. public DelegateCommand(Action executeMethod) : this(executeMethod, (o) => true) { } /// /// Initializes a new instance of . /// /// Delegate to execute when Execute is called on the command. This can be null to just hook up a CanExecute delegate. /// Delegate to execute when CanExecute is called on the command. This can be null. /// When both and are . public DelegateCommand(Action executeMethod, Func canExecuteMethod) : base() { if (executeMethod == null || canExecuteMethod == null) throw new ArgumentNullException(nameof(executeMethod), "Neither the executeMethod nor the canExecuteMethod delegates can be null."); TypeInfo genericTypeInfo = typeof(T).GetTypeInfo(); // DelegateCommand allows object or Nullable<>. // note: Nullable<> is a struct so we cannot use a class constraint. if (genericTypeInfo.IsValueType) { if ((!genericTypeInfo.IsGenericType) || (!typeof(Nullable<>).GetTypeInfo().IsAssignableFrom(genericTypeInfo.GetGenericTypeDefinition().GetTypeInfo()))) { throw new InvalidCastException("T for DelegateCommand<T> is not an object nor Nullable."); } } _executeMethod = executeMethod; _canExecuteMethod = canExecuteMethod; } /// ///Executes the command and invokes the provided during construction. /// ///Data used by the command. public void Execute(T parameter) { try { _executeMethod(parameter); } catch (Exception ex) { if (!ExceptionHandler.CanHandle(ex)) throw; ExceptionHandler.Handle(ex, parameter); } } /// ///Determines if the command can execute by invoked the provided during construction. /// ///Data used by the command to determine if it can execute. /// /// if this command can be executed; otherwise, . /// public bool CanExecute(T parameter) { try { return _canExecuteMethod(parameter); } catch (Exception ex) { if (!ExceptionHandler.CanHandle(ex)) throw; ExceptionHandler.Handle(ex, parameter); return false; } } /// /// Handle the internal invocation of /// /// Command Parameter protected override void Execute(object? parameter) { try { // Note: We don't call Execute because we would potentially invoke the Try/Catch twice. // It is also needed here incase (T)parameter throws the exception _executeMethod((T)parameter!); } catch (Exception ex) { if (!ExceptionHandler.CanHandle(ex)) throw; ExceptionHandler.Handle(ex, parameter); } } /// /// Handle the internal invocation of /// /// /// if the Command Can Execute, otherwise protected override bool CanExecute(object? parameter) { try { // Note: We don't call Execute because we would potentially invoke the Try/Catch twice. // It is also needed here incase (T)parameter throws the exception return CanExecute((T)parameter!); } catch (Exception ex) { if (!ExceptionHandler.CanHandle(ex)) throw; ExceptionHandler.Handle(ex, parameter); return false; } } /// /// Observes a property that implements INotifyPropertyChanged, and automatically calls DelegateCommandBase.RaiseCanExecuteChanged on property changed notifications. /// /// The type of the return value of the method that this delegate encapsulates /// The property expression. Example: ObservesProperty(() => PropertyName). /// The current instance of DelegateCommand public DelegateCommand ObservesProperty(Expression> propertyExpression) { ObservesPropertyInternal(propertyExpression); return this; } /// /// 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. /// /// The property expression. Example: ObservesCanExecute(() => PropertyName). /// The current instance of DelegateCommand public DelegateCommand ObservesCanExecute(Expression> canExecuteExpression) { Expression> expression = System.Linq.Expressions.Expression.Lambda>(canExecuteExpression.Body, System.Linq.Expressions.Expression.Parameter(typeof(T), "o")); _canExecuteMethod = expression.Compile(); ObservesPropertyInternal(canExecuteExpression); return this; } /// /// Registers an callback if an exception is encountered while executing the /// /// The Callback /// The current instance of public DelegateCommand Catch(Action @catch) { ExceptionHandler.Register(@catch); return this; } /// /// Registers an callback if an exception is encountered while executing the /// /// The Callback /// The current instance of public DelegateCommand Catch(Action @catch) { ExceptionHandler.Register(@catch); return this; } /// /// Registers an callback if an exception is encountered while executing the /// /// The Exception Type /// The Callback /// The current instance of public DelegateCommand Catch(Action @catch) where TException : Exception { ExceptionHandler.Register(@catch); return this; } /// /// Registers an callback if an exception is encountered while executing the /// /// The Exception Type /// The Callback /// The current instance of public DelegateCommand Catch(Action @catch) where TException : Exception { ExceptionHandler.Register(@catch); return this; } /// /// Registers an async callback if an exception is encountered while executing the /// /// The Callback /// The current instance of public DelegateCommand Catch(Func @catch) { ExceptionHandler.Register(@catch); return this; } /// /// Registers an async callback if an exception is encountered while executing the /// /// The Callback /// The current instance of public DelegateCommand Catch(Func @catch) { ExceptionHandler.Register(@catch); return this; } /// /// Registers an async callback if an exception is encountered while executing the /// /// The Exception Type /// The Callback /// The current instance of public DelegateCommand Catch(Func @catch) where TException : Exception { ExceptionHandler.Register(@catch); return this; } /// /// Registers an async callback if an exception is encountered while executing the /// /// The Exception Type /// The Callback /// The current instance of public DelegateCommand Catch(Func @catch) where TException : Exception { ExceptionHandler.Register(@catch); return this; } } }