using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Windows.Input;
namespace AIStudio.Wpf.DiagramDesigner.Additionals.Commands
{
///
/// The CompositeCommand composes one or more ICommands.
///
public class CompositeCommand : ICommand
{
private readonly List _registeredCommands = new List();
private readonly bool _monitorCommandActivity;
private readonly EventHandler _onRegisteredCommandCanExecuteChangedHandler;
private SynchronizationContext _synchronizationContext;
///
/// Initializes a new instance of .
///
public CompositeCommand()
{
this._onRegisteredCommandCanExecuteChangedHandler = new EventHandler(this.OnRegisteredCommandCanExecuteChanged);
_synchronizationContext = SynchronizationContext.Current;
}
///
/// Initializes a new instance of .
///
/// Indicates when the command activity is going to be monitored.
public CompositeCommand(bool monitorCommandActivity)
: this()
{
this._monitorCommandActivity = monitorCommandActivity;
}
///
/// Adds a command to the collection and signs up for the event of it.
///
///
/// If this command is set to monitor command activity, and
/// implements the interface, this method will subscribe to its
/// event.
///
/// The command to register.
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;
}
}
}
///
/// Removes a command from the collection and removes itself from the event of it.
///
/// The command to unregister.
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();
}
///
/// Forwards to the registered commands and returns
/// if all of the commands return .
///
/// Data used by the command.
/// If the command does not require data to be passed, this object can be set to .
///
/// if all of the commands return ; otherwise, .
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;
}
///
/// Occurs when any of the registered commands raise .
///
public virtual event EventHandler CanExecuteChanged;
///
/// Forwards to the registered commands.
///
/// Data used by the command.
/// If the command does not require data to be passed, this object can be set to .
///
public virtual void Execute(object parameter)
{
Queue commands;
lock (this._registeredCommands)
{
commands = new Queue(this._registeredCommands.Where(this.ShouldExecute).ToList());
}
while (commands.Count > 0)
{
ICommand command = commands.Dequeue();
command.Execute(parameter);
}
}
///
/// Evaluates if a command should execute.
///
/// The command to evaluate.
/// A value indicating whether the command should be used
/// when evaluating and .
///
/// If this command is set to monitor command activity, and
/// implements the interface,
/// this method will return if the command's
/// property is ; otherwise it always returns .
protected virtual bool ShouldExecute(ICommand command)
{
var activeAwareCommand = command as IActiveAware;
if (this._monitorCommandActivity && activeAwareCommand != null)
{
return activeAwareCommand.IsActive;
}
return true;
}
///
/// Gets the list of all the registered commands.
///
/// A list of registered commands.
/// This returns a copy of the commands subscribed to the CompositeCommand.
public IList RegisteredCommands
{
get
{
IList commandList;
lock (this._registeredCommands)
{
commandList = this._registeredCommands.ToList();
}
return commandList;
}
}
///
/// Raises on the UI thread so every
/// command invoker can requery to check if the
/// can execute.
/// 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);
}
}
///
/// Handler for IsActiveChanged events of registered commands.
///
/// The sender.
/// EventArgs to pass to the event.
private void Command_IsActiveChanged(object sender, EventArgs e)
{
this.OnCanExecuteChanged();
}
}
}