Created
February 12, 2015 18:37
-
-
Save ReedCopsey/ba11d8d691f27ca2710f to your computer and use it in GitHub Desktop.
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
/// <summary> | |
/// An <see cref="ICommand"/> whose delegates do not take any parameters for <see cref="Execute"/> and <see cref="CanExecute"/>. | |
/// </summary> | |
public class ActionCommand : DelegateCommandBase | |
{ | |
/// <summary> | |
/// Initializes a new instance of the <see cref="ActionCommand"/> class with the <see cref="Action"/> to invoke on execution. | |
/// </summary> | |
/// <param name="executeMethod">The execute method.</param> | |
/// <param name="useCommandManager">if set to <c>true</c> use the command manager instead of our own event process for CanExecuteChanged Tracking.</param> | |
public ActionCommand(Action executeMethod, bool useCommandManager = false) | |
: this(executeMethod, () => true, useCommandManager) | |
{ | |
} | |
/// <summary> | |
/// Initializes a new instance of the <see cref="ActionCommand"/> class 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> | |
/// <param name="useCommandManager">if set to <c>true</c> use the command manager instead of our own event process for CanExecuteChanged Tracking.</param> | |
public ActionCommand(Action executeMethod, Func<bool> canExecuteMethod, bool useCommandManager = false) | |
: base(o => executeMethod(), o => canExecuteMethod(), useCommandManager) | |
{ | |
if (executeMethod == null || canExecuteMethod == null) | |
{ | |
throw new ArgumentNullException("executeMethod", "Execute method cannot be null"); | |
} | |
} | |
/// <summary> | |
/// Executes the command. | |
/// </summary> | |
public void Execute() | |
{ | |
this.Execute(null); | |
} | |
/// <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 this.CanExecute(null); | |
} | |
} |
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
/// <summary> | |
/// An <see cref="ICommand"/> whose delegates can be attached for <see cref="Execute"/> and <see cref="CanExecute"/>. | |
/// </summary> | |
/// <typeparam name="T">Parameter type.</typeparam> | |
/// <remarks> | |
/// The constructor deliberately prevent 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 | |
{ | |
/// <summary> | |
/// Initializes a new instance of the <see cref="DelegateCommand{T}"/> class. | |
/// </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="useCommandManager">if set to <c>true</c> use the command manager instead of our own event process for CanExecuteChanged Tracking.</param> | |
/// <remarks><seealso cref="CanExecute"/> will always return true.</remarks> | |
public DelegateCommand(Action<T> executeMethod, bool useCommandManager = false) | |
: this(executeMethod, o => true, useCommandManager) | |
{ | |
} | |
/// <summary> | |
/// Initializes a new instance of the <see cref="DelegateCommand{T}"/> class. | |
/// </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> | |
/// <param name="useCommandManager">if set to <c>true</c> use the command manager instead of our own event process for CanExecuteChanged Tracking.</param> | |
/// <exception cref="ArgumentNullException">When both <paramref name="executeMethod"/> and <paramref name="canExecuteMethod"/> ar <see langword="null" />.</exception> | |
public DelegateCommand(Action<T> executeMethod, Func<T, bool> canExecuteMethod, bool useCommandManager = false) | |
: base((o) => executeMethod((T)o), o => canExecuteMethod((T)o), useCommandManager) | |
{ | |
if (executeMethod == null || canExecuteMethod == null) | |
{ | |
throw new ArgumentNullException("executeMethod", "Execute method cannot be null"); | |
} | |
Type genericType = typeof(T); | |
// DelegateCommand allows object or Nullable<>. | |
// note: Nullable<> is a struct so we cannot use a class constraint. | |
if (genericType.IsValueType) | |
{ | |
if ((!genericType.IsGenericType) || (!typeof(Nullable<>).IsAssignableFrom(genericType.GetGenericTypeDefinition()))) | |
{ | |
throw new InvalidCastException("Only reference or nullable types are supported."); | |
} | |
} | |
} | |
/// <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 this.CanExecute(parameter); | |
} | |
/// <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) | |
{ | |
this.Execute(parameter); | |
} | |
} |
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
/// <summary> | |
/// An <see cref="ICommand"/> whose delegates can be attached for <see cref="Execute"/> and <see cref="CanExecute"/>. | |
/// </summary> | |
public abstract class DelegateCommandBase : IDelegateCommand | |
{ | |
/// <summary> | |
/// The execute method | |
/// </summary> | |
private readonly Action<object> executeMethod; | |
/// <summary> | |
/// The can execute method | |
/// </summary> | |
private readonly Func<object, bool> canExecuteMethod; | |
/// <summary> | |
/// Flag whether to use the command manager, or our own event handling | |
/// </summary> | |
private readonly bool useCommandManager; | |
/// <summary> | |
/// Initializes a new instance of the DelegateCommandBase class, specifying both the execute action and the can execute function. | |
/// </summary> | |
/// <param name="executeMethod">The <see cref="Action"/> to execute when <see cref="ICommand.Execute"/> is invoked.</param> | |
/// <param name="canExecuteMethod">The <see cref="Func{Object,Bool}"/> to invoked when <see cref="ICommand.CanExecute"/> is invoked.</param> | |
/// <param name="useCommandManager">if set to <c>true</c> [use command manager].</param> | |
protected DelegateCommandBase(Action<object> executeMethod, Func<object, bool> canExecuteMethod, bool useCommandManager) | |
{ | |
if (executeMethod == null || canExecuteMethod == null) | |
{ | |
throw new ArgumentNullException("executeMethod", "Execute method cannot be null"); | |
} | |
this.executeMethod = executeMethod; | |
this.canExecuteMethod = canExecuteMethod; | |
this.useCommandManager = useCommandManager; | |
} | |
/// <summary> | |
/// Occurs when changes occur that affect whether or not the command should execute. You must keep a hard | |
/// reference to the handler to avoid garbage collection and unexpected results. | |
/// </summary> | |
public event EventHandler CanExecuteChanged | |
{ | |
add | |
{ | |
if (this.useCommandManager) | |
{ | |
CommandManager.RequerySuggested += value; | |
} | |
else | |
{ | |
CanExecuteChangedWeakEventManager.AddHandler(this, value); | |
} | |
} | |
remove | |
{ | |
if (this.useCommandManager) | |
{ | |
CommandManager.RequerySuggested -= value; | |
} | |
else | |
{ | |
CanExecuteChangedWeakEventManager.RemoveHandler(this, value); | |
} | |
} | |
} | |
private event EventHandler CanExecuteChangedInternal; | |
/// <summary> | |
/// Raises <see cref="DelegateCommandBase.CanExecuteChanged"/> on the UI thread so every command invoker | |
/// can requery to check if the command can execute. | |
/// <remarks>Note that this will trigger the execution of <see cref="DelegateCommandBase.CanExecute"/> once for each invoker.</remarks> | |
/// </summary> | |
public void RaiseCanExecuteChanged() | |
{ | |
this.OnCanExecuteChanged(); | |
} | |
/// <summary> | |
/// Defines the method to be called when the command is invoked. | |
/// </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 null.</param> | |
void ICommand.Execute(object parameter) | |
{ | |
this.Execute(parameter); | |
} | |
/// <summary> | |
/// Defines the method that determines whether the command can execute in its current state. | |
/// </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 null.</param> | |
/// <returns> | |
/// true if this command can be executed; otherwise, false. | |
/// </returns> | |
bool ICommand.CanExecute(object parameter) | |
{ | |
return this.CanExecute(parameter); | |
} | |
/// <summary> | |
/// Raises <see cref="ICommand.CanExecuteChanged"/> on the UI thread so every | |
/// command invoker can requery <see cref="ICommand.CanExecute"/> to check if the | |
/// ICommand can execute. | |
/// </summary> | |
protected virtual void OnCanExecuteChanged() | |
{ | |
if (this.useCommandManager) | |
{ | |
CommandManager.InvalidateRequerySuggested(); | |
} | |
else | |
{ | |
var handler = this.CanExecuteChangedInternal; | |
if (handler != null) | |
{ | |
handler(this, EventArgs.Empty); | |
} | |
} | |
} | |
/// <summary> | |
/// Executes the command with the provided parameter by invoking the <see cref="Action{Object}"/> supplied during construction. | |
/// </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 null.</param> | |
protected void Execute(object parameter) | |
{ | |
this.executeMethod(parameter); | |
} | |
/// <summary> | |
/// Determines if the command can execute with the provided parameter by invoing the <see cref="Func{Object,Bool}"/> supplied during construction. | |
/// </summary> | |
/// <param name="parameter">The parameter to use when determining if this command can execute.</param> | |
/// <returns>Returns <see langword="true"/> if the command can execute. <see langword="False"/> otherwise.</returns> | |
protected bool CanExecute(object parameter) | |
{ | |
return this.canExecuteMethod == null || this.canExecuteMethod(parameter); | |
} | |
/// <summary> | |
/// Internal WeakEventManager to handle the events in a weak way | |
/// </summary> | |
private class CanExecuteChangedWeakEventManager : WeakEventManager | |
{ | |
private CanExecuteChangedWeakEventManager() | |
{ | |
} | |
/// <summary> | |
/// Get the event manager for the current thread. | |
/// </summary> | |
private static CanExecuteChangedWeakEventManager CurrentManager | |
{ | |
get | |
{ | |
Type managerType = typeof(CanExecuteChangedWeakEventManager); | |
CanExecuteChangedWeakEventManager manager = | |
(CanExecuteChangedWeakEventManager)GetCurrentManager(managerType); | |
// at first use, create and register a new manager | |
if (manager == null) | |
{ | |
manager = new CanExecuteChangedWeakEventManager(); | |
WeakEventManager.SetCurrentManager(managerType, manager); | |
} | |
return manager; | |
} | |
} | |
/// <summary> | |
/// Add a handler for the given source's event. | |
/// </summary> | |
public static void AddHandler(DelegateCommandBase source, EventHandler handler) | |
{ | |
if (source == null) | |
{ | |
throw new ArgumentNullException("source"); | |
} | |
if (handler == null) | |
{ | |
throw new ArgumentNullException("handler"); | |
} | |
CurrentManager.ProtectedAddHandler(source, handler); | |
} | |
/// <summary> | |
/// Remove a handler for the given source's event. | |
/// </summary> | |
public static void RemoveHandler(DelegateCommandBase source, EventHandler handler) | |
{ | |
if (source == null) | |
{ | |
throw new ArgumentNullException("source"); | |
} | |
if (handler == null) | |
{ | |
throw new ArgumentNullException("handler"); | |
} | |
CurrentManager.ProtectedRemoveHandler(source, handler); | |
} | |
/// <summary> | |
/// Return a new list to hold listeners to the event. | |
/// </summary> | |
protected override ListenerList NewListenerList() | |
{ | |
return new ListenerList(); | |
} | |
/// <summary> | |
/// Listen to the given source for the event. | |
/// </summary> | |
protected override void StartListening(object source) | |
{ | |
DelegateCommandBase typedSource = (DelegateCommandBase)source; | |
typedSource.CanExecuteChangedInternal += this.OnCanExecuteChangedInternal; | |
} | |
/// <summary> | |
/// Stop listening to the given source for the event. | |
/// </summary> | |
protected override void StopListening(object source) | |
{ | |
DelegateCommandBase typedSource = (DelegateCommandBase)source; | |
typedSource.CanExecuteChangedInternal -= this.OnCanExecuteChangedInternal; | |
} | |
/// <summary> | |
/// Event handler for the SomeEvent event. | |
/// </summary> | |
private void OnCanExecuteChangedInternal(object sender, EventArgs e) | |
{ | |
this.DeliverEvent(sender, e); | |
} | |
} | |
} |
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
/// <summary> | |
/// Interface to extend a command and allow you to handle raising CanExecuteChanged | |
/// </summary> | |
public interface IDelegateCommand : ICommand | |
{ | |
/// <summary> | |
/// Raises <see cref="ICommand.CanExecuteChanged"/> | |
/// <remarks>Note that this will trigger the execution of <see cref="ICommand.CanExecute"/> once for each invoker.</remarks> | |
/// </summary> | |
void RaiseCanExecuteChanged(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment