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;
}
}
}