mirror of
https://gitee.com/akwkevin/aistudio.-wpf.-diagram
synced 2026-04-08 02:06:40 +08:00
整理整理
This commit is contained in:
@@ -0,0 +1,905 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace AIStudio.Wpf.DiagramDesigner.Additionals.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// This class allows delegating the commanding logic to methods passed as parameters,
|
||||
/// and enables a View to bind commands to objects that are not part of the element tree.
|
||||
/// </summary>
|
||||
public class CanExecuteDelegateCommand : IDelegateCommand
|
||||
{
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public CanExecuteDelegateCommand(Action executeMethod)
|
||||
: this(executeMethod, null, false)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public CanExecuteDelegateCommand(Action executeMethod, Func<bool> canExecuteMethod)
|
||||
: this(executeMethod, canExecuteMethod, false)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public CanExecuteDelegateCommand(Action executeMethod, Func<bool> canExecuteMethod, bool isAutomaticRequeryDisabled)
|
||||
{
|
||||
if (executeMethod == null)
|
||||
{
|
||||
throw new ArgumentNullException("executeMethod");
|
||||
}
|
||||
|
||||
_executeMethod = executeMethod;
|
||||
_canExecuteMethod = canExecuteMethod;
|
||||
_isAutomaticRequeryDisabled = isAutomaticRequeryDisabled;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Method to determine if the command can be executed
|
||||
/// </summary>
|
||||
public bool CanExecute()
|
||||
{
|
||||
if (_canExecuteMethod != null)
|
||||
{
|
||||
return _canExecuteMethod();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Execution of the command
|
||||
/// </summary>
|
||||
public void Execute()
|
||||
{
|
||||
if (_executeMethod != null)
|
||||
{
|
||||
_executeMethod();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public object Target
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_executeMethod == null)
|
||||
return null;
|
||||
|
||||
return _executeMethod.Target;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Property to enable or disable CommandManager's automatic requery on this command
|
||||
/// </summary>
|
||||
public bool IsAutomaticRequeryDisabled
|
||||
{
|
||||
get
|
||||
{
|
||||
return _isAutomaticRequeryDisabled;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_isAutomaticRequeryDisabled != value)
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
CommandManagerHelper.RemoveHandlersFromRequerySuggested(_canExecuteChangedHandlers);
|
||||
}
|
||||
else
|
||||
{
|
||||
CommandManagerHelper.AddHandlersToRequerySuggested(_canExecuteChangedHandlers);
|
||||
}
|
||||
_isAutomaticRequeryDisabled = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raises the CanExecuteChaged event
|
||||
/// </summary>
|
||||
public void RaiseCanExecuteChanged()
|
||||
{
|
||||
OnCanExecuteChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Protected virtual method to raise CanExecuteChanged event
|
||||
/// </summary>
|
||||
protected virtual void OnCanExecuteChanged()
|
||||
{
|
||||
CommandManagerHelper.CallWeakReferenceHandlers(_canExecuteChangedHandlers);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ICommand Members
|
||||
|
||||
/// <summary>
|
||||
/// ICommand.CanExecuteChanged implementation
|
||||
/// </summary>
|
||||
public event EventHandler CanExecuteChanged
|
||||
{
|
||||
add
|
||||
{
|
||||
if (!_isAutomaticRequeryDisabled)
|
||||
{
|
||||
CommandManager.RequerySuggested += value;
|
||||
}
|
||||
CommandManagerHelper.AddWeakReferenceHandler(ref _canExecuteChangedHandlers, value, 2);
|
||||
}
|
||||
remove
|
||||
{
|
||||
if (!_isAutomaticRequeryDisabled)
|
||||
{
|
||||
CommandManager.RequerySuggested -= value;
|
||||
}
|
||||
CommandManagerHelper.RemoveWeakReferenceHandler(_canExecuteChangedHandlers, value);
|
||||
}
|
||||
}
|
||||
|
||||
bool ICommand.CanExecute(object parameter)
|
||||
{
|
||||
return CanExecute();
|
||||
}
|
||||
|
||||
void ICommand.Execute(object parameter)
|
||||
{
|
||||
Execute();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Data
|
||||
|
||||
private readonly Action _executeMethod = null;
|
||||
private readonly Func<bool> _canExecuteMethod = null;
|
||||
private bool _isAutomaticRequeryDisabled = false;
|
||||
private List<WeakReference> _canExecuteChangedHandlers;
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class CanExecuteDelegateCommand<T1> : IDelegateCommand
|
||||
{
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public CanExecuteDelegateCommand(Action<T1> executeMethod)
|
||||
: this(executeMethod, null, false)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public CanExecuteDelegateCommand(Action<T1> executeMethod, Func<T1, bool> canExecuteMethod)
|
||||
: this(executeMethod, canExecuteMethod, false)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public CanExecuteDelegateCommand(Action<T1> executeMethod, Func<T1, bool> canExecuteMethod, bool isAutomaticRequeryDisabled)
|
||||
{
|
||||
if (executeMethod == null)
|
||||
{
|
||||
throw new ArgumentNullException("executeMethod");
|
||||
}
|
||||
|
||||
_executeMethod = executeMethod;
|
||||
_canExecuteMethod = canExecuteMethod;
|
||||
_isAutomaticRequeryDisabled = isAutomaticRequeryDisabled;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Method to determine if the command can be executed
|
||||
/// </summary>
|
||||
public bool CanExecute(T1 parameter1)
|
||||
{
|
||||
if (_canExecuteMethod != null)
|
||||
{
|
||||
return _canExecuteMethod(parameter1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Execution of the command
|
||||
/// </summary>
|
||||
public void Execute(T1 parameter1)
|
||||
{
|
||||
if (_executeMethod != null)
|
||||
{
|
||||
_executeMethod(parameter1);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raises the CanExecuteChaged event
|
||||
/// </summary>
|
||||
public void RaiseCanExecuteChanged()
|
||||
{
|
||||
OnCanExecuteChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Protected virtual method to raise CanExecuteChanged event
|
||||
/// </summary>
|
||||
protected virtual void OnCanExecuteChanged()
|
||||
{
|
||||
CommandManagerHelper.CallWeakReferenceHandlers(_canExecuteChangedHandlers);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Property to enable or disable CommandManager's automatic requery on this command
|
||||
/// </summary>
|
||||
public bool IsAutomaticRequeryDisabled
|
||||
{
|
||||
get
|
||||
{
|
||||
return _isAutomaticRequeryDisabled;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_isAutomaticRequeryDisabled != value)
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
CommandManagerHelper.RemoveHandlersFromRequerySuggested(_canExecuteChangedHandlers);
|
||||
}
|
||||
else
|
||||
{
|
||||
CommandManagerHelper.AddHandlersToRequerySuggested(_canExecuteChangedHandlers);
|
||||
}
|
||||
_isAutomaticRequeryDisabled = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public object Target
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_executeMethod == null)
|
||||
return null;
|
||||
|
||||
return _executeMethod.Target;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ICommand Members
|
||||
|
||||
/// <summary>
|
||||
/// ICommand.CanExecuteChanged implementation
|
||||
/// </summary>
|
||||
public event EventHandler CanExecuteChanged
|
||||
{
|
||||
add
|
||||
{
|
||||
if (!_isAutomaticRequeryDisabled)
|
||||
{
|
||||
CommandManager.RequerySuggested += value;
|
||||
}
|
||||
CommandManagerHelper.AddWeakReferenceHandler(ref _canExecuteChangedHandlers, value, 2);
|
||||
}
|
||||
remove
|
||||
{
|
||||
if (!_isAutomaticRequeryDisabled)
|
||||
{
|
||||
CommandManager.RequerySuggested -= value;
|
||||
}
|
||||
CommandManagerHelper.RemoveWeakReferenceHandler(_canExecuteChangedHandlers, value);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool CanExecute(object parameter)
|
||||
{
|
||||
if (parameter == null || (parameter is object[] && (parameter as object[]).Length < 1))
|
||||
return false;
|
||||
|
||||
if (parameter is object[])
|
||||
{
|
||||
object[] parameters = parameter as object[];
|
||||
|
||||
// if T is of value type and the parameter is not
|
||||
// set yet, then return false if CanExecute delegate
|
||||
// exists, else return true
|
||||
if ((parameters[0] != null && !typeof(T1).IsAssignableFrom(parameters[0].GetType()))
|
||||
|| (parameters[0] == null && typeof(T1).IsValueType)
|
||||
)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return CanExecute((T1)parameters[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (parameter != null && !typeof(T1).IsAssignableFrom(parameter.GetType()))
|
||||
parameter = null;
|
||||
|
||||
return CanExecute((T1)parameter);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Execute(object parameter)
|
||||
{
|
||||
//如果T1不允许为空,而parameter参数又为空,则直接返回
|
||||
if (typeof(T1).IsValueType && (parameter == null || (parameter is object[] && (parameter as object[]).Length < 1)))
|
||||
return;
|
||||
|
||||
if (parameter is object[])
|
||||
{
|
||||
object[] parameters = parameter as object[];
|
||||
|
||||
// if T1 is of value type and the parameter is not
|
||||
// set yet, then return false if CanExecute delegate
|
||||
// exists, else return true
|
||||
if ((parameters[0] != null && !typeof(T1).IsAssignableFrom(parameters[0].GetType()))
|
||||
|| (parameters[0] == null && typeof(T1).IsValueType)
|
||||
)
|
||||
return;
|
||||
|
||||
Execute((T1)parameters[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (parameter != null && !typeof(T1).IsAssignableFrom(parameter.GetType()))
|
||||
parameter = null;
|
||||
|
||||
Execute((T1)parameter);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Data
|
||||
|
||||
private readonly Action<T1> _executeMethod = null;
|
||||
private readonly Func<T1, bool> _canExecuteMethod = null;
|
||||
private bool _isAutomaticRequeryDisabled = false;
|
||||
private List<WeakReference> _canExecuteChangedHandlers;
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class CanExecuteDelegateCommand<T1, T2> : IDelegateCommand
|
||||
{
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public CanExecuteDelegateCommand(Action<T1, T2> executeMethod)
|
||||
: this(executeMethod, null, false)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public CanExecuteDelegateCommand(Action<T1, T2> executeMethod, Func<T1, T2, bool> canExecuteMethod)
|
||||
: this(executeMethod, canExecuteMethod, false)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public CanExecuteDelegateCommand(Action<T1, T2> executeMethod, Func<T1, T2, bool> canExecuteMethod, bool isAutomaticRequeryDisabled)
|
||||
{
|
||||
if (executeMethod == null)
|
||||
{
|
||||
throw new ArgumentNullException("executeMethod");
|
||||
}
|
||||
|
||||
_executeMethod = executeMethod;
|
||||
_canExecuteMethod = canExecuteMethod;
|
||||
_isAutomaticRequeryDisabled = isAutomaticRequeryDisabled;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Method to determine if the command can be executed
|
||||
/// </summary>
|
||||
public bool CanExecute(T1 parameter1, T2 parameter2)
|
||||
{
|
||||
if (_canExecuteMethod != null)
|
||||
{
|
||||
return _canExecuteMethod(parameter1, parameter2);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Execution of the command
|
||||
/// </summary>
|
||||
public void Execute(T1 parameter1, T2 parameter2)
|
||||
{
|
||||
if (_executeMethod != null)
|
||||
{
|
||||
_executeMethod(parameter1, parameter2);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raises the CanExecuteChaged event
|
||||
/// </summary>
|
||||
public void RaiseCanExecuteChanged()
|
||||
{
|
||||
OnCanExecuteChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Protected virtual method to raise CanExecuteChanged event
|
||||
/// </summary>
|
||||
protected virtual void OnCanExecuteChanged()
|
||||
{
|
||||
CommandManagerHelper.CallWeakReferenceHandlers(_canExecuteChangedHandlers);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Property to enable or disable CommandManager's automatic requery on this command
|
||||
/// </summary>
|
||||
public bool IsAutomaticRequeryDisabled
|
||||
{
|
||||
get
|
||||
{
|
||||
return _isAutomaticRequeryDisabled;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_isAutomaticRequeryDisabled != value)
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
CommandManagerHelper.RemoveHandlersFromRequerySuggested(_canExecuteChangedHandlers);
|
||||
}
|
||||
else
|
||||
{
|
||||
CommandManagerHelper.AddHandlersToRequerySuggested(_canExecuteChangedHandlers);
|
||||
}
|
||||
_isAutomaticRequeryDisabled = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public object Target
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_executeMethod == null)
|
||||
return null;
|
||||
|
||||
return _executeMethod.Target;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ICommand Members
|
||||
|
||||
/// <summary>
|
||||
/// ICommand.CanExecuteChanged implementation
|
||||
/// </summary>
|
||||
public event EventHandler CanExecuteChanged
|
||||
{
|
||||
add
|
||||
{
|
||||
if (!_isAutomaticRequeryDisabled)
|
||||
{
|
||||
CommandManager.RequerySuggested += value;
|
||||
}
|
||||
CommandManagerHelper.AddWeakReferenceHandler(ref _canExecuteChangedHandlers, value, 2);
|
||||
}
|
||||
remove
|
||||
{
|
||||
if (!_isAutomaticRequeryDisabled)
|
||||
{
|
||||
CommandManager.RequerySuggested -= value;
|
||||
}
|
||||
CommandManagerHelper.RemoveWeakReferenceHandler(_canExecuteChangedHandlers, value);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool CanExecute(object parameter)
|
||||
{
|
||||
if (parameter == null || !(parameter is object[]) || (parameter as object[]).Length < 2)
|
||||
return false;
|
||||
|
||||
object[] parameters = parameter as object[];
|
||||
|
||||
// if T is of value type and the parameter is not
|
||||
// set yet, then return false if CanExecute delegate
|
||||
// exists, else return true
|
||||
if ((parameters[0] != null && !typeof(T1).IsAssignableFrom(parameters[0].GetType()))
|
||||
|| (parameters[0] == null && typeof(T1).IsValueType)
|
||||
|| (parameters[1] != null && !typeof(T2).IsAssignableFrom(parameters[1].GetType()))
|
||||
|| (parameters[1] == null && typeof(T2).IsValueType)
|
||||
)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return CanExecute((T1)parameters[0], (T2)parameters[1]);
|
||||
}
|
||||
|
||||
public virtual void Execute(object parameter)
|
||||
{
|
||||
if (parameter == null || !(parameter is object[]) || (parameter as object[]).Length < 2)
|
||||
return;
|
||||
|
||||
object[] parameters = parameter as object[];
|
||||
|
||||
// if T is of value type and the parameter is not
|
||||
// set yet, then return false if CanExecute delegate
|
||||
// exists, else return true
|
||||
if ((parameters[0] != null && !typeof(T1).IsAssignableFrom(parameters[0].GetType()))
|
||||
|| (parameters[0] == null && typeof(T1).IsValueType)
|
||||
|| (parameters[1] != null && !typeof(T2).IsAssignableFrom(parameters[1].GetType()))
|
||||
|| (parameters[1] == null && typeof(T2).IsValueType)
|
||||
)
|
||||
return;
|
||||
|
||||
Execute((T1)parameters[0], (T2)parameters[1]);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Data
|
||||
|
||||
private readonly Action<T1, T2> _executeMethod = null;
|
||||
private readonly Func<T1, T2, bool> _canExecuteMethod = null;
|
||||
private bool _isAutomaticRequeryDisabled = false;
|
||||
private List<WeakReference> _canExecuteChangedHandlers;
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class CanExecuteDelegateCommand<T1, T2, T3> : IDelegateCommand
|
||||
{
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public CanExecuteDelegateCommand(Action<T1, T2, T3> executeMethod)
|
||||
: this(executeMethod, null, false)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public CanExecuteDelegateCommand(Action<T1, T2, T3> executeMethod, Func<T1, T2, T3, bool> canExecuteMethod)
|
||||
: this(executeMethod, canExecuteMethod, false)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public CanExecuteDelegateCommand(Action<T1, T2, T3> executeMethod, Func<T1, T2, T3, bool> canExecuteMethod, bool isAutomaticRequeryDisabled)
|
||||
{
|
||||
if (executeMethod == null)
|
||||
{
|
||||
throw new ArgumentNullException("executeMethod");
|
||||
}
|
||||
|
||||
_executeMethod = executeMethod;
|
||||
_canExecuteMethod = canExecuteMethod;
|
||||
_isAutomaticRequeryDisabled = isAutomaticRequeryDisabled;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Method to determine if the command can be executed
|
||||
/// </summary>
|
||||
public bool CanExecute(T1 parameter1, T2 parameter2, T3 parameter3)
|
||||
{
|
||||
if (_canExecuteMethod != null)
|
||||
{
|
||||
return _canExecuteMethod(parameter1, parameter2, parameter3);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Execution of the command
|
||||
/// </summary>
|
||||
public void Execute(T1 parameter1, T2 parameter2, T3 parameter3)
|
||||
{
|
||||
if (_executeMethod != null)
|
||||
{
|
||||
_executeMethod(parameter1, parameter2, parameter3);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raises the CanExecuteChaged event
|
||||
/// </summary>
|
||||
public void RaiseCanExecuteChanged()
|
||||
{
|
||||
OnCanExecuteChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Protected virtual method to raise CanExecuteChanged event
|
||||
/// </summary>
|
||||
protected virtual void OnCanExecuteChanged()
|
||||
{
|
||||
CommandManagerHelper.CallWeakReferenceHandlers(_canExecuteChangedHandlers);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Property to enable or disable CommandManager's automatic requery on this command
|
||||
/// </summary>
|
||||
public bool IsAutomaticRequeryDisabled
|
||||
{
|
||||
get
|
||||
{
|
||||
return _isAutomaticRequeryDisabled;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_isAutomaticRequeryDisabled != value)
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
CommandManagerHelper.RemoveHandlersFromRequerySuggested(_canExecuteChangedHandlers);
|
||||
}
|
||||
else
|
||||
{
|
||||
CommandManagerHelper.AddHandlersToRequerySuggested(_canExecuteChangedHandlers);
|
||||
}
|
||||
_isAutomaticRequeryDisabled = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public object Target
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_executeMethod == null)
|
||||
return null;
|
||||
|
||||
return _executeMethod.Target;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ICommand Members
|
||||
|
||||
/// <summary>
|
||||
/// ICommand.CanExecuteChanged implementation
|
||||
/// </summary>
|
||||
public event EventHandler CanExecuteChanged
|
||||
{
|
||||
add
|
||||
{
|
||||
if (!_isAutomaticRequeryDisabled)
|
||||
{
|
||||
CommandManager.RequerySuggested += value;
|
||||
}
|
||||
CommandManagerHelper.AddWeakReferenceHandler(ref _canExecuteChangedHandlers, value, 2);
|
||||
}
|
||||
remove
|
||||
{
|
||||
if (!_isAutomaticRequeryDisabled)
|
||||
{
|
||||
CommandManager.RequerySuggested -= value;
|
||||
}
|
||||
CommandManagerHelper.RemoveWeakReferenceHandler(_canExecuteChangedHandlers, value);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool CanExecute(object parameter)
|
||||
{
|
||||
if (parameter == null || !(parameter is object[]) || (parameter as object[]).Length < 3)
|
||||
return false;
|
||||
|
||||
object[] parameters = parameter as object[];
|
||||
|
||||
// if T is of value type and the parameter is not
|
||||
// set yet, then return false if CanExecute delegate
|
||||
// exists, else return true
|
||||
if ((parameters[0] != null && !typeof(T1).IsAssignableFrom(parameters[0].GetType()))
|
||||
|| (parameters[0] == null && typeof(T1).IsValueType)
|
||||
|| (parameters[1] != null && !typeof(T2).IsAssignableFrom(parameters[1].GetType()))
|
||||
|| (parameters[1] == null && typeof(T2).IsValueType)
|
||||
|| (parameters[2] != null && !typeof(T3).IsAssignableFrom(parameters[2].GetType()))
|
||||
|| (parameters[2] == null && typeof(T3).IsValueType)
|
||||
)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return CanExecute((T1)parameters[0], (T2)parameters[1], (T3)parameters[2]);
|
||||
}
|
||||
|
||||
public virtual void Execute(object parameter)
|
||||
{
|
||||
if (parameter == null || !(parameter is object[]) || (parameter as object[]).Length < 3)
|
||||
return;
|
||||
|
||||
object[] parameters = parameter as object[];
|
||||
|
||||
// if T is of value type and the parameter is not
|
||||
// set yet, then return false if CanExecute delegate
|
||||
// exists, else return true
|
||||
if ((parameters[0] != null && !typeof(T1).IsAssignableFrom(parameters[0].GetType()))
|
||||
|| (parameters[0] == null && typeof(T1).IsValueType)
|
||||
|| (parameters[1] != null && !typeof(T2).IsAssignableFrom(parameters[1].GetType()))
|
||||
|| (parameters[1] == null && typeof(T2).IsValueType)
|
||||
|| (parameters[2] != null && !typeof(T3).IsAssignableFrom(parameters[2].GetType()))
|
||||
|| (parameters[2] == null && typeof(T3).IsValueType)
|
||||
)
|
||||
return;
|
||||
|
||||
Execute((T1)parameters[0], (T2)parameters[1], (T3)parameters[2]);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Data
|
||||
|
||||
private readonly Action<T1, T2, T3> _executeMethod = null;
|
||||
private readonly Func<T1, T2, T3, bool> _canExecuteMethod = null;
|
||||
private bool _isAutomaticRequeryDisabled = false;
|
||||
private List<WeakReference> _canExecuteChangedHandlers;
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public interface IDelegateCommand : ICommand
|
||||
{
|
||||
object Target { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This class contains methods for the CommandManager that help avoid memory leaks by
|
||||
/// using weak references.
|
||||
/// </summary>
|
||||
internal class CommandManagerHelper
|
||||
{
|
||||
internal static void CallWeakReferenceHandlers(List<WeakReference> handlers)
|
||||
{
|
||||
if (handlers != null)
|
||||
{
|
||||
// Take a snapshot of the handlers before we call out to them since the handlers
|
||||
// could cause the array to me modified while we are reading it.
|
||||
|
||||
EventHandler[] callees = new EventHandler[handlers.Count];
|
||||
int count = 0;
|
||||
|
||||
for (int i = handlers.Count - 1; i >= 0; i--)
|
||||
{
|
||||
WeakReference reference = handlers[i];
|
||||
EventHandler handler = reference.Target as EventHandler;
|
||||
if (handler == null)
|
||||
{
|
||||
// Clean up old handlers that have been collected
|
||||
handlers.RemoveAt(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
callees[count] = handler;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
// Call the handlers that we snapshotted
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
EventHandler handler = callees[i];
|
||||
handler(null, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static void AddHandlersToRequerySuggested(List<WeakReference> handlers)
|
||||
{
|
||||
if (handlers != null)
|
||||
{
|
||||
foreach (WeakReference handlerRef in handlers)
|
||||
{
|
||||
EventHandler handler = handlerRef.Target as EventHandler;
|
||||
if (handler != null)
|
||||
{
|
||||
CommandManager.RequerySuggested += handler;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static void RemoveHandlersFromRequerySuggested(List<WeakReference> handlers)
|
||||
{
|
||||
if (handlers != null)
|
||||
{
|
||||
foreach (WeakReference handlerRef in handlers)
|
||||
{
|
||||
EventHandler handler = handlerRef.Target as EventHandler;
|
||||
if (handler != null)
|
||||
{
|
||||
CommandManager.RequerySuggested -= handler;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static void AddWeakReferenceHandler(ref List<WeakReference> handlers, EventHandler handler)
|
||||
{
|
||||
AddWeakReferenceHandler(ref handlers, handler, -1);
|
||||
}
|
||||
|
||||
internal static void AddWeakReferenceHandler(ref List<WeakReference> handlers, EventHandler handler, int defaultListSize)
|
||||
{
|
||||
if (handlers == null)
|
||||
{
|
||||
handlers = (defaultListSize > 0 ? new List<WeakReference>(defaultListSize) : new List<WeakReference>());
|
||||
}
|
||||
|
||||
handlers.Add(new WeakReference(handler));
|
||||
}
|
||||
|
||||
internal static void RemoveWeakReferenceHandler(List<WeakReference> handlers, EventHandler handler)
|
||||
{
|
||||
if (handlers != null)
|
||||
{
|
||||
for (int i = handlers.Count - 1; i >= 0; i--)
|
||||
{
|
||||
WeakReference reference = handlers[i];
|
||||
EventHandler existingHandler = reference.Target as EventHandler;
|
||||
if ((existingHandler == null) || (existingHandler == handler))
|
||||
{
|
||||
// Clean up old handlers that have been collected
|
||||
// in addition to the handler that is to be removed.
|
||||
handlers.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace AIStudio.Wpf.DiagramDesigner.Additionals.Commands
|
||||
{
|
||||
public class CommandReference : Freezable, ICommand
|
||||
{
|
||||
public CommandReference()
|
||||
{
|
||||
// Blank
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty CommandProperty = DependencyProperty.Register("Command", typeof(ICommand), typeof(CommandReference), new PropertyMetadata(new PropertyChangedCallback(OnCommandChanged)));
|
||||
|
||||
public ICommand Command
|
||||
{
|
||||
get { return (ICommand)GetValue(CommandProperty); }
|
||||
set { SetValue(CommandProperty, value); }
|
||||
}
|
||||
|
||||
#region ICommand Members
|
||||
|
||||
public bool CanExecute(object parameter)
|
||||
{
|
||||
if (Command != null)
|
||||
return Command.CanExecute(parameter);
|
||||
return false;
|
||||
}
|
||||
|
||||
public void Execute(object parameter)
|
||||
{
|
||||
Command.Execute(parameter);
|
||||
}
|
||||
|
||||
public event EventHandler CanExecuteChanged;
|
||||
|
||||
private static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
CommandReference commandReference = d as CommandReference;
|
||||
ICommand oldCommand = e.OldValue as ICommand;
|
||||
ICommand newCommand = e.NewValue as ICommand;
|
||||
|
||||
if (oldCommand != null)
|
||||
{
|
||||
oldCommand.CanExecuteChanged -= commandReference.CanExecuteChanged;
|
||||
}
|
||||
if (newCommand != null)
|
||||
{
|
||||
newCommand.CanExecuteChanged += commandReference.CanExecuteChanged;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Freezable
|
||||
|
||||
protected override Freezable CreateInstanceCore()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace AIStudio.Wpf.DiagramDesigner.Additionals.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// The CompositeCommand composes one or more ICommands.
|
||||
/// </summary>
|
||||
public class CompositeCommand : ICommand
|
||||
{
|
||||
private readonly List<ICommand> _registeredCommands = new List<ICommand>();
|
||||
private readonly bool _monitorCommandActivity;
|
||||
private readonly EventHandler _onRegisteredCommandCanExecuteChangedHandler;
|
||||
private SynchronizationContext _synchronizationContext;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="CompositeCommand"/>.
|
||||
/// </summary>
|
||||
public CompositeCommand()
|
||||
{
|
||||
this._onRegisteredCommandCanExecuteChangedHandler = new EventHandler(this.OnRegisteredCommandCanExecuteChanged);
|
||||
_synchronizationContext = SynchronizationContext.Current;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="CompositeCommand"/>.
|
||||
/// </summary>
|
||||
/// <param name="monitorCommandActivity">Indicates when the command activity is going to be monitored.</param>
|
||||
public CompositeCommand(bool monitorCommandActivity)
|
||||
: this()
|
||||
{
|
||||
this._monitorCommandActivity = monitorCommandActivity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a command to the collection and signs up for the <see cref="ICommand.CanExecuteChanged"/> event of it.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If this command is set to monitor command activity, and <paramref name="command"/>
|
||||
/// implements the <see cref="IActiveAware"/> interface, this method will subscribe to its
|
||||
/// <see cref="IActiveAware.IsActiveChanged"/> event.
|
||||
/// </remarks>
|
||||
/// <param name="command">The command to register.</param>
|
||||
public virtual void RegisterCommand(ICommand command)
|
||||
{
|
||||
if (command == null) throw new ArgumentNullException(nameof(command));
|
||||
if (command == this)
|
||||
{
|
||||
throw new ArgumentException("CannotRegisterCompositeCommandInItself");
|
||||
}
|
||||
|
||||
lock (this._registeredCommands)
|
||||
{
|
||||
if (this._registeredCommands.Contains(command))
|
||||
{
|
||||
throw new InvalidOperationException("CannotRegisterSameCommandTwice");
|
||||
}
|
||||
this._registeredCommands.Add(command);
|
||||
}
|
||||
|
||||
command.CanExecuteChanged += this._onRegisteredCommandCanExecuteChangedHandler;
|
||||
this.OnCanExecuteChanged();
|
||||
|
||||
if (this._monitorCommandActivity)
|
||||
{
|
||||
var activeAwareCommand = command as IActiveAware;
|
||||
if (activeAwareCommand != null)
|
||||
{
|
||||
activeAwareCommand.IsActiveChanged += this.Command_IsActiveChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a command from the collection and removes itself from the <see cref="ICommand.CanExecuteChanged"/> event of it.
|
||||
/// </summary>
|
||||
/// <param name="command">The command to unregister.</param>
|
||||
public virtual void UnregisterCommand(ICommand command)
|
||||
{
|
||||
if (command == null) throw new ArgumentNullException(nameof(command));
|
||||
bool removed;
|
||||
lock (this._registeredCommands)
|
||||
{
|
||||
removed = this._registeredCommands.Remove(command);
|
||||
}
|
||||
|
||||
if (removed)
|
||||
{
|
||||
command.CanExecuteChanged -= this._onRegisteredCommandCanExecuteChangedHandler;
|
||||
this.OnCanExecuteChanged();
|
||||
|
||||
if (this._monitorCommandActivity)
|
||||
{
|
||||
var activeAwareCommand = command as IActiveAware;
|
||||
if (activeAwareCommand != null)
|
||||
{
|
||||
activeAwareCommand.IsActiveChanged -= this.Command_IsActiveChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnRegisteredCommandCanExecuteChanged(object sender, EventArgs e)
|
||||
{
|
||||
this.OnCanExecuteChanged();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Forwards <see cref="ICommand.CanExecute"/> to the registered commands and returns
|
||||
/// <see langword="true" /> if all of the commands return <see langword="true" />.
|
||||
/// </summary>
|
||||
/// <param name="parameter">Data used by the command.
|
||||
/// If the command does not require data to be passed, this object can be set to <see langword="null" />.
|
||||
/// </param>
|
||||
/// <returns><see langword="true" /> if all of the commands return <see langword="true" />; otherwise, <see langword="false" />.</returns>
|
||||
public virtual bool CanExecute(object parameter)
|
||||
{
|
||||
bool hasEnabledCommandsThatShouldBeExecuted = false;
|
||||
|
||||
ICommand[] commandList;
|
||||
lock (this._registeredCommands)
|
||||
{
|
||||
commandList = this._registeredCommands.ToArray();
|
||||
}
|
||||
foreach (ICommand command in commandList)
|
||||
{
|
||||
if (this.ShouldExecute(command))
|
||||
{
|
||||
if (!command.CanExecute(parameter))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
hasEnabledCommandsThatShouldBeExecuted = true;
|
||||
}
|
||||
}
|
||||
|
||||
return hasEnabledCommandsThatShouldBeExecuted;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when any of the registered commands raise <see cref="ICommand.CanExecuteChanged"/>.
|
||||
/// </summary>
|
||||
public virtual event EventHandler CanExecuteChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Forwards <see cref="ICommand.Execute"/> to the registered commands.
|
||||
/// </summary>
|
||||
/// <param name="parameter">Data used by the command.
|
||||
/// If the command does not require data to be passed, this object can be set to <see langword="null" />.
|
||||
/// </param>
|
||||
public virtual void Execute(object parameter)
|
||||
{
|
||||
Queue<ICommand> commands;
|
||||
lock (this._registeredCommands)
|
||||
{
|
||||
commands = new Queue<ICommand>(this._registeredCommands.Where(this.ShouldExecute).ToList());
|
||||
}
|
||||
|
||||
while (commands.Count > 0)
|
||||
{
|
||||
ICommand command = commands.Dequeue();
|
||||
command.Execute(parameter);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates if a command should execute.
|
||||
/// </summary>
|
||||
/// <param name="command">The command to evaluate.</param>
|
||||
/// <returns>A <see cref="bool"/> value indicating whether the command should be used
|
||||
/// when evaluating <see cref="CompositeCommand.CanExecute"/> and <see cref="CompositeCommand.Execute"/>.</returns>
|
||||
/// <remarks>
|
||||
/// If this command is set to monitor command activity, and <paramref name="command"/>
|
||||
/// implements the <see cref="IActiveAware"/> interface,
|
||||
/// this method will return <see langword="false" /> if the command's <see cref="IActiveAware.IsActive"/>
|
||||
/// property is <see langword="false" />; otherwise it always returns <see langword="true" />.</remarks>
|
||||
protected virtual bool ShouldExecute(ICommand command)
|
||||
{
|
||||
var activeAwareCommand = command as IActiveAware;
|
||||
|
||||
if (this._monitorCommandActivity && activeAwareCommand != null)
|
||||
{
|
||||
return activeAwareCommand.IsActive;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of all the registered commands.
|
||||
/// </summary>
|
||||
/// <value>A list of registered commands.</value>
|
||||
/// <remarks>This returns a copy of the commands subscribed to the CompositeCommand.</remarks>
|
||||
public IList<ICommand> RegisteredCommands
|
||||
{
|
||||
get
|
||||
{
|
||||
IList<ICommand> commandList;
|
||||
lock (this._registeredCommands)
|
||||
{
|
||||
commandList = this._registeredCommands.ToList();
|
||||
}
|
||||
|
||||
return commandList;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raises <see cref="ICommand.CanExecuteChanged"/> on the UI thread so every
|
||||
/// command invoker can requery <see cref="ICommand.CanExecute"/> to check if the
|
||||
/// <see cref="CompositeCommand"/> can execute.
|
||||
/// </summary>a
|
||||
protected virtual void OnCanExecuteChanged()
|
||||
{
|
||||
var handler = CanExecuteChanged;
|
||||
if (handler != null)
|
||||
{
|
||||
if (_synchronizationContext != null && _synchronizationContext != SynchronizationContext.Current)
|
||||
_synchronizationContext.Post((o) => handler.Invoke(this, EventArgs.Empty), null);
|
||||
else
|
||||
handler.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for IsActiveChanged events of registered commands.
|
||||
/// </summary>
|
||||
/// <param name="sender">The sender.</param>
|
||||
/// <param name="e">EventArgs to pass to the event.</param>
|
||||
private void Command_IsActiveChanged(object sender, EventArgs e)
|
||||
{
|
||||
this.OnCanExecuteChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,333 @@
|
||||
using System.Collections;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Markup;
|
||||
|
||||
namespace AIStudio.Wpf.DiagramDesigner.Additionals.Commands
|
||||
{
|
||||
[MarkupExtensionReturnType(typeof(object))]
|
||||
public class ControlBinding : MarkupExtension
|
||||
{
|
||||
/// <summary>
|
||||
/// 是否禁止处理绑定
|
||||
/// </summary>
|
||||
public static bool Disabled = false;
|
||||
|
||||
#region Properties
|
||||
|
||||
public IValueConverter Converter { get; set; }
|
||||
public object ConverterParameter { get; set; }
|
||||
public string ElementName { get; set; }
|
||||
public RelativeSource RelativeSource { get; set; }
|
||||
public object Source { get; set; }
|
||||
public bool ValidatesOnDataErrors { get; set; }
|
||||
public bool ValidatesOnExceptions { get; set; }
|
||||
public bool NotifyOnValidationError { get; set; }
|
||||
public object TargetNullValue { get; set; }
|
||||
public object FallBackValue { get; set; }
|
||||
public string StringFormat { get; set; }
|
||||
public UpdateSourceTrigger UpdateSourceTrigger { get; set; }
|
||||
public Collection<ValidationRule> ValidationRules { get; set; }
|
||||
|
||||
public BindingMode Mode { get; set; }
|
||||
private int AncestorLevel = 1;
|
||||
[ConstructorArgument("path")]
|
||||
public PropertyPath Path { get; set; }
|
||||
[TypeConverter(typeof(CultureInfoIetfLanguageTagConverter))]
|
||||
public CultureInfo ConverterCulture { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
public ControlBinding()
|
||||
{
|
||||
Mode = BindingMode.Default;
|
||||
ValidationRules = new Collection<ValidationRule>();
|
||||
}
|
||||
|
||||
public ControlBinding(PropertyPath path)
|
||||
{
|
||||
Path = path;
|
||||
Mode = BindingMode.Default;
|
||||
UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
|
||||
NotifyOnValidationError = true;
|
||||
ValidatesOnDataErrors = true;
|
||||
ValidatesOnExceptions = true;
|
||||
TargetNullValue = null;
|
||||
ValidationRules = new Collection<ValidationRule>();
|
||||
}
|
||||
|
||||
public ControlBinding(PropertyPath path, string elementName)
|
||||
: this(path)
|
||||
{
|
||||
Path = path;
|
||||
|
||||
if (elementName == "$")
|
||||
AncestorLevel = 2;
|
||||
else
|
||||
ElementName = elementName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取代理Binding对象
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
internal Binding GetBinding(object rootObject)
|
||||
{
|
||||
Binding binding = new Binding();
|
||||
binding.StringFormat = StringFormat;
|
||||
binding.Path = Path;
|
||||
binding.Mode = Mode;
|
||||
binding.Converter = Converter;
|
||||
binding.ConverterCulture = ConverterCulture;
|
||||
binding.ConverterParameter = ConverterParameter;
|
||||
binding.UpdateSourceTrigger = UpdateSourceTrigger;
|
||||
if (TargetNullValue != null) binding.TargetNullValue = TargetNullValue;
|
||||
if (FallBackValue != null)
|
||||
binding.FallbackValue = FallBackValue;
|
||||
//单向绑定不需要进行数据校验
|
||||
NotifyOnValidationError &= (Mode != BindingMode.OneWay && Mode != BindingMode.OneTime);
|
||||
|
||||
binding.ValidatesOnDataErrors = NotifyOnValidationError;
|
||||
binding.ValidatesOnExceptions = NotifyOnValidationError;
|
||||
binding.NotifyOnValidationError = NotifyOnValidationError;
|
||||
|
||||
|
||||
//添加ValidationRule
|
||||
foreach (ValidationRule vr in ValidationRules)
|
||||
binding.ValidationRules.Add(vr);
|
||||
|
||||
if (ElementName != null)
|
||||
binding.ElementName = ElementName;
|
||||
if (Source != null)
|
||||
{
|
||||
if (!(Source is string) || (Source as string) != "$")
|
||||
binding.Source = Source;
|
||||
}
|
||||
if (RelativeSource != null)
|
||||
binding.RelativeSource = RelativeSource;
|
||||
|
||||
if (string.IsNullOrEmpty(ElementName) && Source == null && RelativeSource == null)
|
||||
{
|
||||
if (rootObject == null)
|
||||
{
|
||||
RelativeSource = new RelativeSource { AncestorLevel = AncestorLevel, AncestorType = typeof(UserControl), Mode = RelativeSourceMode.FindAncestor };
|
||||
|
||||
binding.RelativeSource = RelativeSource;
|
||||
}
|
||||
else
|
||||
{
|
||||
binding.Source = rootObject;
|
||||
if (rootObject is UserControl || rootObject is Window)
|
||||
{
|
||||
if (binding.Path == null || binding.Path.Path == ".")
|
||||
binding.Path = new PropertyPath("DataContext");
|
||||
else
|
||||
binding.Path = new PropertyPath("DataContext." + binding.Path.Path, binding.Path.PathParameters);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return binding;
|
||||
}
|
||||
|
||||
public override object ProvideValue(System.IServiceProvider serviceProvider)
|
||||
{
|
||||
var valueProvider = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
|
||||
if (valueProvider == null)
|
||||
return null;
|
||||
|
||||
|
||||
//当使用ControlMultiBinding或者MultiBinding时
|
||||
if (valueProvider.TargetProperty == null && valueProvider.TargetObject is ICollection)
|
||||
{
|
||||
if (valueProvider.TargetObject is Collection<ControlBinding>
|
||||
|| valueProvider.TargetObject.GetType().FullName == "MS.Internal.Data.BindingCollection")
|
||||
return this;
|
||||
}
|
||||
|
||||
//如果禁用
|
||||
if (ControlBinding.Disabled)
|
||||
return (valueProvider.TargetProperty as DependencyProperty).DefaultMetadata.DefaultValue;
|
||||
|
||||
var rootObjProvider = serviceProvider.GetService(typeof(System.Xaml.IRootObjectProvider)) as System.Xaml.IRootObjectProvider;
|
||||
|
||||
#region 如果是针对WinForm的ControlBinding
|
||||
|
||||
if (valueProvider.TargetObject is System.Windows.Forms.Control && rootObjProvider != null)
|
||||
{
|
||||
System.Windows.Forms.Control ctl = valueProvider.TargetObject as System.Windows.Forms.Control;
|
||||
System.Windows.Forms.Binding b = new System.Windows.Forms.Binding((valueProvider.TargetProperty as PropertyInfo).Name, rootObjProvider.RootObject, Path.Path);
|
||||
if (Mode == BindingMode.OneWay)
|
||||
{
|
||||
b.ControlUpdateMode = System.Windows.Forms.ControlUpdateMode.OnPropertyChanged;
|
||||
b.DataSourceUpdateMode = System.Windows.Forms.DataSourceUpdateMode.Never;
|
||||
}
|
||||
else if (Mode == BindingMode.OneWayToSource)
|
||||
{
|
||||
b.ControlUpdateMode = System.Windows.Forms.ControlUpdateMode.Never;
|
||||
if (UpdateSourceTrigger == System.Windows.Data.UpdateSourceTrigger.PropertyChanged
|
||||
|| UpdateSourceTrigger == System.Windows.Data.UpdateSourceTrigger.Default)
|
||||
b.DataSourceUpdateMode = System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged;
|
||||
else if (UpdateSourceTrigger == System.Windows.Data.UpdateSourceTrigger.LostFocus)
|
||||
b.DataSourceUpdateMode = System.Windows.Forms.DataSourceUpdateMode.OnValidation;
|
||||
}
|
||||
else if (Mode == BindingMode.TwoWay || Mode == BindingMode.Default)
|
||||
{
|
||||
b.ControlUpdateMode = System.Windows.Forms.ControlUpdateMode.OnPropertyChanged;
|
||||
b.DataSourceUpdateMode = System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged;
|
||||
}
|
||||
else if (Mode == BindingMode.OneTime)
|
||||
{
|
||||
//这里应该赋值一次:OneTime
|
||||
//......
|
||||
|
||||
b.ControlUpdateMode = System.Windows.Forms.ControlUpdateMode.Never;
|
||||
b.DataSourceUpdateMode = System.Windows.Forms.DataSourceUpdateMode.Never;
|
||||
}
|
||||
|
||||
ctl.DataBindings.Add(b);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
var bindingTarget = valueProvider.TargetObject as DependencyObject;
|
||||
var bindingProperty = valueProvider.TargetProperty as DependencyProperty;
|
||||
if (bindingProperty == null)
|
||||
return null;
|
||||
|
||||
#region 使用代理Binding返回最终的值
|
||||
|
||||
//得到Binding对象
|
||||
Binding binding = GetBinding(rootObjProvider == null ? null : rootObjProvider.RootObject);
|
||||
if (binding == null) return null;
|
||||
|
||||
object retValue = binding.ProvideValue(serviceProvider);
|
||||
|
||||
#endregion
|
||||
|
||||
return retValue;
|
||||
}
|
||||
}
|
||||
|
||||
[ContentProperty("Bindings")]
|
||||
public class ControlMultiBinding : MarkupExtension
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public Collection<ControlBinding> Bindings { get; private set; }
|
||||
|
||||
public IMultiValueConverter Converter { get; set; }
|
||||
|
||||
[DefaultValue("")]
|
||||
[TypeConverter(typeof(CultureInfoIetfLanguageTagConverter))]
|
||||
public CultureInfo ConverterCulture { get; set; }
|
||||
|
||||
[DefaultValue("")]
|
||||
public object ConverterParameter { get; set; }
|
||||
|
||||
public BindingMode Mode { get; set; }
|
||||
|
||||
[DefaultValue(false)]
|
||||
public bool NotifyOnSourceUpdated { get; set; }
|
||||
|
||||
[DefaultValue(false)]
|
||||
public bool NotifyOnTargetUpdated { get; set; }
|
||||
|
||||
[DefaultValue(false)]
|
||||
public bool NotifyOnValidationError { get; set; }
|
||||
|
||||
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
||||
public UpdateSourceExceptionFilterCallback UpdateSourceExceptionFilter { get; set; }
|
||||
|
||||
public UpdateSourceTrigger UpdateSourceTrigger { get; set; }
|
||||
|
||||
[DefaultValue(false)]
|
||||
public bool ValidatesOnDataErrors { get; set; }
|
||||
|
||||
[DefaultValue(false)]
|
||||
public bool ValidatesOnExceptions { get; set; }
|
||||
|
||||
public Collection<ValidationRule> ValidationRules { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
public ControlMultiBinding()
|
||||
{
|
||||
Bindings = new Collection<ControlBinding>();
|
||||
ValidationRules = new Collection<ValidationRule>();
|
||||
Mode = BindingMode.Default;
|
||||
UpdateSourceTrigger = System.Windows.Data.UpdateSourceTrigger.Default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取代理MultiBindings对象
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
internal MultiBinding GetMultiBindings(object rootObject)
|
||||
{
|
||||
MultiBinding multiBindings = new MultiBinding();
|
||||
|
||||
if (this.Converter != null)
|
||||
multiBindings.Converter = this.Converter;
|
||||
if (this.ConverterCulture != null)
|
||||
multiBindings.ConverterCulture = this.ConverterCulture;
|
||||
if (this.ConverterParameter != null)
|
||||
multiBindings.ConverterParameter = this.ConverterParameter;
|
||||
if (this.UpdateSourceExceptionFilter != null)
|
||||
multiBindings.UpdateSourceExceptionFilter = this.UpdateSourceExceptionFilter;
|
||||
multiBindings.Mode = this.Mode;
|
||||
multiBindings.NotifyOnSourceUpdated = this.NotifyOnSourceUpdated;
|
||||
multiBindings.NotifyOnTargetUpdated = this.NotifyOnTargetUpdated;
|
||||
multiBindings.NotifyOnValidationError = this.NotifyOnValidationError;
|
||||
multiBindings.UpdateSourceTrigger = this.UpdateSourceTrigger;
|
||||
multiBindings.ValidatesOnDataErrors = this.ValidatesOnDataErrors;
|
||||
multiBindings.ValidatesOnExceptions = this.ValidatesOnExceptions;
|
||||
|
||||
foreach (object bd in Bindings)
|
||||
{
|
||||
if (bd is BindingBase)
|
||||
multiBindings.Bindings.Add(bd as BindingBase);
|
||||
else if (bd is ControlBinding)
|
||||
multiBindings.Bindings.Add((bd as ControlBinding).GetBinding(rootObject) as BindingBase);
|
||||
}
|
||||
|
||||
foreach (ValidationRule vr in ValidationRules)
|
||||
multiBindings.ValidationRules.Add(vr);
|
||||
|
||||
return multiBindings;
|
||||
}
|
||||
|
||||
public override object ProvideValue(System.IServiceProvider serviceProvider)
|
||||
{
|
||||
var valueProvider = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
|
||||
if (valueProvider == null)
|
||||
return null;
|
||||
|
||||
var bindingTarget = valueProvider.TargetObject as DependencyObject;
|
||||
var bindingProperty = valueProvider.TargetProperty as DependencyProperty;
|
||||
if (bindingProperty == null)
|
||||
return null;
|
||||
|
||||
//使用MultiBindings返回最终的值
|
||||
#region 使用代理Binding返回最终的值
|
||||
|
||||
var rootObjProvider = serviceProvider.GetService(typeof(System.Xaml.IRootObjectProvider)) as System.Xaml.IRootObjectProvider;
|
||||
|
||||
MultiBinding multiBindings = GetMultiBindings(rootObjProvider == null ? null : rootObjProvider.RootObject);
|
||||
if (multiBindings == null) return null;
|
||||
|
||||
object retValue = multiBindings.ProvideValue(serviceProvider);
|
||||
|
||||
#endregion
|
||||
|
||||
return retValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
using System;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace AIStudio.Wpf.DiagramDesigner.Additionals.Commands
|
||||
{
|
||||
#region ConbrolCommand
|
||||
|
||||
public interface IControlCommand
|
||||
{
|
||||
void SetControl(Control cb);
|
||||
|
||||
void SetBase(IControlCommand cc);
|
||||
|
||||
void Execute(object parameter);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ControlBase可以使用的命令类
|
||||
/// 使用此类,可以使Command具有继承的机制,在新的Command中可以调用旧的Command
|
||||
/// </summary>
|
||||
public class ControlCommand<T1> : CanExecuteDelegateCommand<ControlCommand<T1>, T1>, IControlCommand
|
||||
{
|
||||
public Control Control { get; protected set; }
|
||||
|
||||
public IControlCommand Base { get; protected set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public ControlCommand(Action<ControlCommand<T1>, T1> executeMethod, Func<ControlCommand<T1>, T1, bool> canExecuteMethod = null, bool isAutomaticRequeryDisabled = false, string name = "")
|
||||
: base(executeMethod, canExecuteMethod, isAutomaticRequeryDisabled)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
|
||||
public override bool CanExecute(object parameter)
|
||||
{
|
||||
return base.CanExecute(new object[] { this, parameter });
|
||||
}
|
||||
|
||||
public override void Execute(object parameter)
|
||||
{
|
||||
base.Execute(new object[] { this, parameter });
|
||||
}
|
||||
|
||||
public virtual void ExecuteBaseCommand(T1 parameter)
|
||||
{
|
||||
if (Base != null)
|
||||
Base.Execute((object)parameter);
|
||||
}
|
||||
|
||||
public void SetControl(Control cb)
|
||||
{
|
||||
Control = cb;
|
||||
}
|
||||
|
||||
public void SetBase(IControlCommand cc)
|
||||
{
|
||||
Base = cc;
|
||||
}
|
||||
}
|
||||
|
||||
public class ControlCommand : CanExecuteDelegateCommand<ControlCommand>, IControlCommand
|
||||
{
|
||||
public Control Control { get; protected set; }
|
||||
|
||||
public IControlCommand Base { get; protected set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public ControlCommand(Action<ControlCommand> executeMethod, Func<ControlCommand, bool> canExecuteMethod = null, bool isAutomaticRequeryDisabled = false, string name = "")
|
||||
: base(executeMethod, canExecuteMethod, isAutomaticRequeryDisabled)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
|
||||
public override bool CanExecute(object parameter)
|
||||
{
|
||||
return base.CanExecute(new object[] { this });
|
||||
}
|
||||
|
||||
public override void Execute(object parameter)
|
||||
{
|
||||
base.Execute(new object[] { this });
|
||||
}
|
||||
|
||||
public virtual void ExecuteBaseCommand()
|
||||
{
|
||||
if (Base != null)
|
||||
Base.Execute((object)null);
|
||||
}
|
||||
|
||||
public void SetControl(Control cb)
|
||||
{
|
||||
Control = cb;
|
||||
}
|
||||
|
||||
public void SetBase(IControlCommand cc)
|
||||
{
|
||||
Base = cc;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AIStudio.Wpf.DiagramDesigner.Additionals.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// An <see cref="ICommand"/> whose delegates do not take any parameters for <see cref="Execute()"/> and <see cref="CanExecute()"/>.
|
||||
/// </summary>
|
||||
/// <see cref="DelegateCommandBase"/>
|
||||
/// <see cref="DelegateCommand{T}"/>
|
||||
public class DelegateCommand : DelegateCommandBase
|
||||
{
|
||||
Action _executeMethod;
|
||||
Func<bool> _canExecuteMethod;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="DelegateCommand"/> with the <see cref="Action"/> to invoke on execution.
|
||||
/// </summary>
|
||||
/// <param name="executeMethod">The <see cref="Action"/> to invoke when <see cref="ICommand.Execute(object)"/> is called.</param>
|
||||
public DelegateCommand(Action executeMethod)
|
||||
: this(executeMethod, () => true)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="DelegateCommand"/> with the <see cref="Action"/> to invoke on execution
|
||||
/// and a <see langword="Func" /> to query for determining if the command can execute.
|
||||
/// </summary>
|
||||
/// <param name="executeMethod">The <see cref="Action"/> to invoke when <see cref="ICommand.Execute"/> is called.</param>
|
||||
/// <param name="canExecuteMethod">The <see cref="Func{TResult}"/> to invoke when <see cref="ICommand.CanExecute"/> is called</param>
|
||||
public DelegateCommand(Action executeMethod, Func<bool> canExecuteMethod)
|
||||
: base()
|
||||
{
|
||||
if (executeMethod == null || canExecuteMethod == null)
|
||||
throw new ArgumentNullException(nameof(executeMethod), "DelegateCommandDelegatesCannotBeNull");
|
||||
|
||||
_executeMethod = executeMethod;
|
||||
_canExecuteMethod = canExecuteMethod;
|
||||
}
|
||||
|
||||
///<summary>
|
||||
/// Executes the command.
|
||||
///</summary>
|
||||
public void Execute()
|
||||
{
|
||||
_executeMethod();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the command can be executed.
|
||||
/// </summary>
|
||||
/// <returns>Returns <see langword="true"/> if the command can execute,otherwise returns <see langword="false"/>.</returns>
|
||||
public bool CanExecute()
|
||||
{
|
||||
return _canExecuteMethod();
|
||||
}
|
||||
|
||||
/// <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();
|
||||
}
|
||||
|
||||
/// <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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Observes a property that implements INotifyPropertyChanged, and automatically calls DelegateCommandBase.RaiseCanExecuteChanged on property changed notifications.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The object type containing the property specified in the expression.</typeparam>
|
||||
/// <param name="propertyExpression">The property expression. Example: ObservesProperty(() => PropertyName).</param>
|
||||
/// <returns>The current instance of DelegateCommand</returns>
|
||||
public DelegateCommand ObservesProperty<T>(Expression<Func<T>> 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 ObservesCanExecute(Expression<Func<bool>> canExecuteExpression)
|
||||
{
|
||||
_canExecuteMethod = canExecuteExpression.Compile();
|
||||
ObservesPropertyInternal(canExecuteExpression);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace AIStudio.Wpf.DiagramDesigner.Additionals.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// An <see cref="ICommand"/> whose delegates can be attached for <see cref="Execute"/> and <see cref="CanExecute"/>.
|
||||
/// </summary>
|
||||
public abstract class DelegateCommandBase : ICommand, IActiveAware
|
||||
{
|
||||
private bool _isActive;
|
||||
|
||||
private SynchronizationContext _synchronizationContext;
|
||||
private readonly HashSet<string> _observedPropertiesExpressions = new HashSet<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of a <see cref="DelegateCommandBase"/>, specifying both the execute action and the can execute function.
|
||||
/// </summary>
|
||||
protected DelegateCommandBase()
|
||||
{
|
||||
_synchronizationContext = SynchronizationContext.Current;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when changes occur that affect whether or not the command should execute.
|
||||
/// </summary>
|
||||
public virtual event EventHandler CanExecuteChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Raises <see cref="ICommand.CanExecuteChanged"/> so every
|
||||
/// command invoker can requery <see cref="ICommand.CanExecute"/>.
|
||||
/// </summary>
|
||||
protected virtual void OnCanExecuteChanged()
|
||||
{
|
||||
var handler = CanExecuteChanged;
|
||||
if (handler != null)
|
||||
{
|
||||
if (_synchronizationContext != null && _synchronizationContext != SynchronizationContext.Current)
|
||||
_synchronizationContext.Post((o) => handler.Invoke(this, EventArgs.Empty), null);
|
||||
else
|
||||
handler.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raises <see cref="CanExecuteChanged"/> so every command invoker
|
||||
/// can requery to check if the command can execute.
|
||||
/// </summary>
|
||||
/// <remarks>Note that this will trigger the execution of <see cref="CanExecuteChanged"/> once for each invoker.</remarks>
|
||||
[SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate")]
|
||||
public void RaiseCanExecuteChanged()
|
||||
{
|
||||
OnCanExecuteChanged();
|
||||
}
|
||||
|
||||
void ICommand.Execute(object parameter)
|
||||
{
|
||||
Execute(parameter);
|
||||
}
|
||||
|
||||
bool ICommand.CanExecute(object parameter)
|
||||
{
|
||||
return CanExecute(parameter);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle the internal invocation of <see cref="ICommand.Execute(object)"/>
|
||||
/// </summary>
|
||||
/// <param name="parameter">Command Parameter</param>
|
||||
protected abstract void Execute(object 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 abstract bool CanExecute(object parameter);
|
||||
|
||||
/// <summary>
|
||||
/// Observes a property that implements INotifyPropertyChanged, and automatically calls DelegateCommandBase.RaiseCanExecuteChanged on property changed notifications.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The object type containing the property specified in the expression.</typeparam>
|
||||
/// <param name="propertyExpression">The property expression. Example: ObservesProperty(() => PropertyName).</param>
|
||||
protected internal void ObservesPropertyInternal<T>(Expression<Func<T>> propertyExpression)
|
||||
{
|
||||
if (_observedPropertiesExpressions.Contains(propertyExpression.ToString()))
|
||||
{
|
||||
throw new ArgumentException($"{propertyExpression.ToString()} is already being observed.",
|
||||
nameof(propertyExpression));
|
||||
}
|
||||
else
|
||||
{
|
||||
_observedPropertiesExpressions.Add(propertyExpression.ToString());
|
||||
PropertyObserver.Observes(propertyExpression, RaiseCanExecuteChanged);
|
||||
}
|
||||
}
|
||||
|
||||
#region IsActive
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the object is active.
|
||||
/// </summary>
|
||||
/// <value><see langword="true" /> if the object is active; otherwise <see langword="false" />.</value>
|
||||
public bool IsActive
|
||||
{
|
||||
get { return _isActive; }
|
||||
set
|
||||
{
|
||||
if (_isActive != value)
|
||||
{
|
||||
_isActive = value;
|
||||
OnIsActiveChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fired if the <see cref="IsActive"/> property changes.
|
||||
/// </summary>
|
||||
public virtual event EventHandler IsActiveChanged;
|
||||
|
||||
/// <summary>
|
||||
/// This raises the <see cref="DelegateCommandBase.IsActiveChanged"/> event.
|
||||
/// </summary>
|
||||
protected virtual void OnIsActiveChanged()
|
||||
{
|
||||
IsActiveChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AIStudio.Wpf.DiagramDesigner.Additionals.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface that defines if the object instance is active
|
||||
/// and notifies when the activity changes.
|
||||
/// </summary>
|
||||
public interface IActiveAware
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the object is active.
|
||||
/// </summary>
|
||||
/// <value><see langword="true" /> if the object is active; otherwise <see langword="false" />.</value>
|
||||
bool IsActive { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Notifies that the value for <see cref="IsActive"/> property has changed.
|
||||
/// </summary>
|
||||
event EventHandler IsActiveChanged;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
|
||||
namespace AIStudio.Wpf.DiagramDesigner.Additionals.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Provide a way to observe property changes of INotifyPropertyChanged objects and invokes a
|
||||
/// custom action when the PropertyChanged event is fired.
|
||||
/// </summary>
|
||||
internal class PropertyObserver
|
||||
{
|
||||
private readonly Action _action;
|
||||
|
||||
private PropertyObserver(System.Linq.Expressions.Expression propertyExpression, Action action)
|
||||
{
|
||||
_action = action;
|
||||
SubscribeListeners(propertyExpression);
|
||||
}
|
||||
|
||||
private void SubscribeListeners(System.Linq.Expressions.Expression propertyExpression)
|
||||
{
|
||||
var propNameStack = new Stack<PropertyInfo>();
|
||||
while (propertyExpression is MemberExpression temp) // Gets the root of the property chain.
|
||||
{
|
||||
propertyExpression = temp.Expression;
|
||||
propNameStack.Push(temp.Member as PropertyInfo); // Records the member info as property info
|
||||
}
|
||||
|
||||
if (!(propertyExpression is ConstantExpression constantExpression))
|
||||
throw new NotSupportedException("Operation not supported for the given expression type. " +
|
||||
"Only MemberExpression and ConstantExpression are currently supported.");
|
||||
|
||||
var propObserverNodeRoot = new PropertyObserverNode(propNameStack.Pop(), _action);
|
||||
PropertyObserverNode previousNode = propObserverNodeRoot;
|
||||
foreach (var propName in propNameStack) // Create a node chain that corresponds to the property chain.
|
||||
{
|
||||
var currentNode = new PropertyObserverNode(propName, _action);
|
||||
previousNode.Next = currentNode;
|
||||
previousNode = currentNode;
|
||||
}
|
||||
|
||||
object propOwnerObject = constantExpression.Value;
|
||||
|
||||
if (!(propOwnerObject is INotifyPropertyChanged inpcObject))
|
||||
throw new InvalidOperationException("Trying to subscribe PropertyChanged listener in object that " +
|
||||
$"owns '{propObserverNodeRoot.PropertyInfo.Name}' property, but the object does not implements INotifyPropertyChanged.");
|
||||
|
||||
propObserverNodeRoot.SubscribeListenerFor(inpcObject);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Observes a property that implements INotifyPropertyChanged, and automatically calls a custom action on
|
||||
/// property changed notifications. The given expression must be in this form: "() => Prop.NestedProp.PropToObserve".
|
||||
/// </summary>
|
||||
/// <param name="propertyExpression">Expression representing property to be observed. Ex.: "() => Prop.NestedProp.PropToObserve".</param>
|
||||
/// <param name="action">Action to be invoked when PropertyChanged event occours.</param>
|
||||
internal static PropertyObserver Observes<T>(Expression<Func<T>> propertyExpression, Action action)
|
||||
{
|
||||
return new PropertyObserver(propertyExpression.Body, action);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AIStudio.Wpf.DiagramDesigner.Additionals.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents each node of nested properties expression and takes care of
|
||||
/// subscribing/unsubscribing INotifyPropertyChanged.PropertyChanged listeners on it.
|
||||
/// </summary>
|
||||
internal class PropertyObserverNode
|
||||
{
|
||||
private readonly Action _action;
|
||||
private INotifyPropertyChanged _inpcObject;
|
||||
|
||||
public PropertyInfo PropertyInfo { get; }
|
||||
public PropertyObserverNode Next { get; set; }
|
||||
|
||||
public PropertyObserverNode(PropertyInfo propertyInfo, Action action)
|
||||
{
|
||||
PropertyInfo = propertyInfo ?? throw new ArgumentNullException(nameof(propertyInfo));
|
||||
_action = () =>
|
||||
{
|
||||
action?.Invoke();
|
||||
if (Next == null) return;
|
||||
Next.UnsubscribeListener();
|
||||
GenerateNextNode();
|
||||
};
|
||||
}
|
||||
|
||||
public void SubscribeListenerFor(INotifyPropertyChanged inpcObject)
|
||||
{
|
||||
_inpcObject = inpcObject;
|
||||
_inpcObject.PropertyChanged += OnPropertyChanged;
|
||||
|
||||
if (Next != null) GenerateNextNode();
|
||||
}
|
||||
|
||||
private void GenerateNextNode()
|
||||
{
|
||||
var nextProperty = PropertyInfo.GetValue(_inpcObject);
|
||||
if (nextProperty == null) return;
|
||||
if (!(nextProperty is INotifyPropertyChanged nextInpcObject))
|
||||
throw new InvalidOperationException("Trying to subscribe PropertyChanged listener in object that " +
|
||||
$"owns '{Next.PropertyInfo.Name}' property, but the object does not implements INotifyPropertyChanged.");
|
||||
|
||||
Next.SubscribeListenerFor(nextInpcObject);
|
||||
}
|
||||
|
||||
private void UnsubscribeListener()
|
||||
{
|
||||
if (_inpcObject != null)
|
||||
_inpcObject.PropertyChanged -= OnPropertyChanged;
|
||||
|
||||
Next?.UnsubscribeListener();
|
||||
}
|
||||
|
||||
private void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e?.PropertyName == PropertyInfo.Name || string.IsNullOrEmpty(e?.PropertyName))
|
||||
{
|
||||
_action?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user