Skip to content

Instantly share code, notes, and snippets.

@Nico-VanHaaster
Created March 6, 2017 04:45
Show Gist options
  • Save Nico-VanHaaster/cedd82fdb53487fcb930fe2f526960b8 to your computer and use it in GitHub Desktop.
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.
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
}
}
@andriyshevchenko
Copy link

Very nice, man

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment