Created
March 6, 2017 04:45
-
-
Save Nico-VanHaaster/cedd82fdb53487fcb930fe2f526960b8 to your computer and use it in GitHub Desktop.
This is a very useful delegate command for WPF. Note I did not write this and cant locate where I found the original source.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Windows.Input; | |
namespace DelegateCommands | |
{ | |
/// <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 DelegateCommand : ICommand | |
{ | |
#region cTor | |
/// <summary> | |
/// private constructor | |
/// </summary> | |
private DelegateCommand() | |
{ | |
} | |
/// <summary> | |
/// Creates a new delegate command by passing in the execute method when the command is executed | |
/// </summary> | |
/// <param name="executeMethod">the execute method</param> | |
public DelegateCommand(Action executeMethod) | |
: this(executeMethod, null, false) | |
{ | |
} | |
/// <summary> | |
/// Creates a new delegate command by passing in the execute method when the command is executed with a call back method to enable \ disable the execution | |
/// </summary> | |
/// <param name="executeMethod">the execute method</param> | |
/// <param name="canExecuteMethod">the function to execute to determine if the command can be run</param> | |
public DelegateCommand(Action executeMethod, Func<bool> canExecuteMethod) | |
: this(executeMethod, canExecuteMethod, false) | |
{ | |
} | |
/// <summary> | |
/// Creates a new delegate command by passing in the execute method when the command is executed with a call back method to enable \ disable the execution. Set the Automatic Requery Flag to disable command requery | |
/// </summary> | |
/// <param name="executeMethod">the execute method</param> | |
/// <param name="canExecuteMethod">the function to execute to determine if the command can be run</param> | |
/// <param name="isAutomaticRequeryDisabled">flag to set the automatic requery method</param> | |
public DelegateCommand(Action executeMethod, Func<bool> canExecuteMethod, bool isAutomaticRequeryDisabled) | |
{ | |
if (executeMethod == null) | |
throw new ArgumentNullException("executeMethod"); | |
_executeMethod = executeMethod; | |
_canExecuteMethod = canExecuteMethod; | |
_isAutomaticRequeryDisabled = isAutomaticRequeryDisabled; | |
} | |
#endregion | |
#region Methods | |
/// <summary> | |
/// Method to determine if the command can be executed | |
/// </summary> | |
public bool CanExecute() | |
{ | |
if (_canExecuteMethod != null) | |
{ | |
return _canExecuteMethod(); | |
} | |
return true; | |
} | |
/// <summary> | |
/// The commands routed event handler | |
/// </summary> | |
/// <param name="sender"></param> | |
/// <param name="e"></param> | |
public void CanExecuteRoutedEventHandler(object sender, CanExecuteRoutedEventArgs e) | |
{ | |
e.CanExecute = this.CanExecute(); | |
} | |
/// <summary> | |
/// Execution of the command | |
/// </summary> | |
public void Execute() | |
{ | |
if (_executeMethod != null) | |
{ | |
_executeMethod(); | |
} | |
} | |
public void ExecutedRoutedEventHandler(object sender, ExecutedRoutedEventArgs e) | |
{ | |
this.Execute(); | |
} | |
/// <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); | |
} | |
} | |
/// <summary> | |
/// ICommand.CanExecute implementation | |
/// </summary> | |
/// <param name="parameter">the object parameter</param> | |
/// <returns></returns> | |
bool ICommand.CanExecute(object parameter) | |
{ | |
return CanExecute(); | |
} | |
/// <summary> | |
/// ICommand.Execute implenetation | |
/// </summary> | |
/// <param name="parameter">the command parameter</param> | |
void ICommand.Execute(object parameter) | |
{ | |
Execute(); | |
} | |
#endregion | |
#region Fields | |
/// <summary> | |
/// the _execution method | |
/// </summary> | |
private readonly Action _executeMethod = null; | |
/// <summary> | |
/// the can execute function | |
/// </summary> | |
private readonly Func<bool> _canExecuteMethod = null; | |
/// <summary> | |
/// automatic requery disabled flag | |
/// </summary> | |
private bool _isAutomaticRequeryDisabled = false; | |
/// <summary> | |
/// can execute changed handlers | |
/// </summary> | |
private List<WeakReference> _canExecuteChangedHandlers; | |
#endregion | |
} | |
/// <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> | |
/// <typeparam name="T">Type of the parameter passed to the delegates</typeparam> | |
public class DelegateCommand<T> : ICommand | |
{ | |
#region Constructors | |
/// <summary> | |
/// private constructor | |
/// </summary> | |
private DelegateCommand() | |
{ | |
throw new Exception("private constructor"); | |
} | |
/// <summary> | |
/// Creates a new delegate command by passing in the execute method when the command is executed | |
/// </summary> | |
/// <param name="executeMethod">the execute method</param> | |
public DelegateCommand(Action<T> executeMethod) | |
: this(executeMethod, null, false) | |
{ | |
} | |
/// <summary> | |
/// Creates a new delegate command by passing in the execute method when the command is executed with a call back method to enable \ disable the execution | |
/// </summary> | |
/// <param name="executeMethod">the execute method</param> | |
/// <param name="canExecuteMethod">the function to execute to determine if the command can be run</param> | |
public DelegateCommand(Action<T> executeMethod, Func<T, bool> canExecuteMethod) | |
: this(executeMethod, canExecuteMethod, false) | |
{ | |
} | |
/// <summary> | |
/// Creates a new delegate command by passing in the execute method when the command is executed with a call back method to enable \ disable the execution. Set the Automatic Requery Flag to disable command requery | |
/// </summary> | |
/// <param name="executeMethod">the execute method</param> | |
/// <param name="canExecuteMethod">the function to execute to determine if the command can be run</param> | |
/// <param name="isAutomaticRequeryDisabled">flag to set the automatic requery method</param> | |
public DelegateCommand(Action<T> executeMethod, Func<T, bool> canExecuteMethod, bool isAutomaticRequeryDisabled) | |
{ | |
if (executeMethod == null) | |
{ | |
throw new ArgumentNullException("executeMethod"); | |
} | |
_executeMethod = executeMethod; | |
_canExecuteMethod = canExecuteMethod; | |
_isAutomaticRequeryDisabled = isAutomaticRequeryDisabled; | |
} | |
#endregion | |
#region Methods | |
/// <summary> | |
/// Method to determine if the command can be executed | |
/// </summary> | |
/// <param name="parameter">the object parameter (optional)</param> | |
public bool CanExecute(T parameter) | |
{ | |
if (_canExecuteMethod != null) | |
return _canExecuteMethod(parameter); | |
return true; | |
} | |
/// <summary> | |
/// Execution of the command | |
/// </summary> | |
/// <param name="parameter">the object parameter (optional)</param> | |
public void Execute(T parameter) | |
{ | |
if (_executeMethod != null) | |
_executeMethod(parameter); | |
} | |
/// <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 Properties | |
/// <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; | |
} | |
} | |
} | |
#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); | |
} | |
} | |
/// <summary> | |
/// ICommand.CanExecute | |
/// </summary> | |
/// <param name="parameter">the object paramter</param> | |
/// <returns></returns> | |
bool ICommand.CanExecute(object parameter) | |
{ | |
if (parameter == null && | |
typeof(T).IsValueType) | |
return true; | |
return CanExecute((T)parameter); | |
} | |
/// <summary> | |
/// ICommand.Execute method | |
/// </summary> | |
/// <param name="parameter">the parameter</param> | |
void ICommand.Execute(object parameter) | |
{ | |
Execute((T)parameter); | |
} | |
#endregion | |
#region Fields | |
/// <summary> | |
/// the execute method | |
/// </summary> | |
private readonly Action<T> _executeMethod = null; | |
/// <summary> | |
/// the can execute method | |
/// </summary> | |
private readonly Func<T, bool> _canExecuteMethod = null; | |
/// <summary> | |
/// flag if automatic requery is disabled | |
/// </summary> | |
private bool _isAutomaticRequeryDisabled = false; | |
/// <summary> | |
/// can execute changed handlers | |
/// </summary> | |
private List<WeakReference> _canExecuteChangedHandlers; | |
#endregion | |
} | |
/// <summary> | |
/// This class contains methods for the CommandManager that help avoid memory leaks by | |
/// using weak references. | |
/// </summary> | |
internal class CommandManagerHelper | |
{ | |
#region Methods | |
/// <summary> | |
/// calls the weak reference handlers | |
/// </summary> | |
/// <param name="handlers">list of handlers</param> | |
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); | |
} | |
} | |
} | |
/// <summary> | |
/// Adds handlers to the request suggested actions | |
/// </summary> | |
/// <param name="handlers">a reference of weak handlers</param> | |
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; | |
} | |
} | |
} | |
} | |
/// <summary> | |
/// removes handlers from the request suggested | |
/// </summary> | |
/// <param name="handlers">the list</param> | |
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; | |
} | |
} | |
} | |
} | |
/// <summary> | |
/// adds a weak reference handler | |
/// </summary> | |
/// <param name="handlers">the list</param> | |
/// <param name="handler">the event handler</param> | |
internal static void AddWeakReferenceHandler(ref List<WeakReference> handlers, EventHandler handler) | |
{ | |
AddWeakReferenceHandler(ref handlers, handler, -1); | |
} | |
/// <summary> | |
/// adds a weak reference handler | |
/// </summary> | |
/// <param name="handlers">the handlers</param> | |
/// <param name="handler">the handler</param> | |
/// <param name="defaultListSize">default size</param> | |
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)); | |
} | |
/// <summary> | |
/// Removes a weak referance handler | |
/// </summary> | |
/// <param name="handlers">the handlers</param> | |
/// <param name="handler">the handler</param> | |
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); | |
} | |
} | |
} | |
} | |
#endregion | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Very nice, man