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