mirror of
https://gitee.com/akwkevin/aistudio.-wpf.-diagram
synced 2026-03-03 00:00:57 +08:00
141 lines
6.7 KiB
C#
141 lines
6.7 KiB
C#
using System;
|
|
using System.Linq.Expressions;
|
|
using System.Reflection;
|
|
using System.Windows.Input;
|
|
|
|
namespace AIStudio.Wpf.DiagramDesigner.Additionals.Commands
|
|
{
|
|
/// <summary>
|
|
/// An <see cref="ICommand"/> whose delegates can be attached for <see cref="Execute(T)"/> and <see cref="CanExecute(T)"/>.
|
|
/// </summary>
|
|
/// <typeparam name="T">Parameter type.</typeparam>
|
|
/// <remarks>
|
|
/// 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.
|
|
/// <para/>
|
|
/// Instead, callers should support a value type by using a nullable value type and checking the HasValue property before using the Value property.
|
|
/// <example>
|
|
/// <code>
|
|
/// public MyClass()
|
|
/// {
|
|
/// this.submitCommand = new DelegateCommand<int?>(this.Submit, this.CanSubmit);
|
|
/// }
|
|
///
|
|
/// private bool CanSubmit(int? customerId)
|
|
/// {
|
|
/// return (customerId.HasValue && customers.Contains(customerId.Value));
|
|
/// }
|
|
/// </code>
|
|
/// </example>
|
|
/// </remarks>
|
|
public class DelegateCommand<T> : DelegateCommandBase
|
|
{
|
|
readonly Action<T> _executeMethod;
|
|
Func<T, bool> _canExecuteMethod;
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of <see cref="DelegateCommand{T}"/>.
|
|
/// </summary>
|
|
/// <param name="executeMethod">Delegate to execute when Execute is called on the command. This can be null to just hook up a CanExecute delegate.</param>
|
|
/// <remarks><see cref="CanExecute(T)"/> will always return true.</remarks>
|
|
public DelegateCommand(Action<T> executeMethod)
|
|
: this(executeMethod, (o) => true)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of <see cref="DelegateCommand{T}"/>.
|
|
/// </summary>
|
|
/// <param name="executeMethod">Delegate to execute when Execute is called on the command. This can be null to just hook up a CanExecute delegate.</param>
|
|
/// <param name="canExecuteMethod">Delegate to execute when CanExecute is called on the command. This can be null.</param>
|
|
/// <exception cref="ArgumentNullException">When both <paramref name="executeMethod"/> and <paramref name="canExecuteMethod"/> are <see langword="null" />.</exception>
|
|
public DelegateCommand(Action<T> executeMethod, Func<T, bool> canExecuteMethod)
|
|
: base()
|
|
{
|
|
if (executeMethod == null || canExecuteMethod == null)
|
|
throw new ArgumentNullException(nameof(executeMethod), "DelegateCommandDelegatesCannotBeNull");
|
|
|
|
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("DelegateCommandInvalidGenericPayloadType");
|
|
}
|
|
}
|
|
|
|
_executeMethod = executeMethod;
|
|
_canExecuteMethod = canExecuteMethod;
|
|
}
|
|
|
|
///<summary>
|
|
///Executes the command and invokes the <see cref="Action{T}"/> provided during construction.
|
|
///</summary>
|
|
///<param name="parameter">Data used by the command.</param>
|
|
public void Execute(T parameter)
|
|
{
|
|
_executeMethod(parameter);
|
|
}
|
|
|
|
///<summary>
|
|
///Determines if the command can execute by invoked the <see cref="Func{T,Bool}"/> provided during construction.
|
|
///</summary>
|
|
///<param name="parameter">Data used by the command to determine if it can execute.</param>
|
|
///<returns>
|
|
///<see langword="true" /> if this command can be executed; otherwise, <see langword="false" />.
|
|
///</returns>
|
|
public bool CanExecute(T parameter)
|
|
{
|
|
return _canExecuteMethod(parameter);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handle the internal invocation of <see cref="ICommand.Execute(object)"/>
|
|
/// </summary>
|
|
/// <param name="parameter">Command Parameter</param>
|
|
protected override void Execute(object parameter)
|
|
{
|
|
Execute((T)parameter);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handle the internal invocation of <see cref="ICommand.CanExecute(object)"/>
|
|
/// </summary>
|
|
/// <param name="parameter"></param>
|
|
/// <returns><see langword="true"/> if the Command Can Execute, otherwise <see langword="false" /></returns>
|
|
protected override bool CanExecute(object parameter)
|
|
{
|
|
return CanExecute((T)parameter);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Observes a property that implements INotifyPropertyChanged, and automatically calls DelegateCommandBase.RaiseCanExecuteChanged on property changed notifications.
|
|
/// </summary>
|
|
/// <typeparam name="TType">The type of the return value of the method that this delegate encapulates</typeparam>
|
|
/// <param name="propertyExpression">The property expression. Example: ObservesProperty(() => PropertyName).</param>
|
|
/// <returns>The current instance of DelegateCommand</returns>
|
|
public DelegateCommand<T> ObservesProperty<TType>(Expression<Func<TType>> propertyExpression)
|
|
{
|
|
ObservesPropertyInternal(propertyExpression);
|
|
return this;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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>
|
|
/// <param name="canExecuteExpression">The property expression. Example: ObservesCanExecute(() => PropertyName).</param>
|
|
/// <returns>The current instance of DelegateCommand</returns>
|
|
public DelegateCommand<T> ObservesCanExecute(Expression<Func<bool>> canExecuteExpression)
|
|
{
|
|
Expression<Func<T, bool>> expression = System.Linq.Expressions.Expression.Lambda<Func<T, bool>>(canExecuteExpression.Body, System.Linq.Expressions.Expression.Parameter(typeof(T), "o"));
|
|
_canExecuteMethod = expression.Compile();
|
|
ObservesPropertyInternal(canExecuteExpression);
|
|
return this;
|
|
}
|
|
}
|
|
}
|