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(); } } }