Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 30 additions & 1 deletion src/Prism.Core/Commands/CompositeCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,19 @@ namespace Prism.Commands
/// <summary>
/// The CompositeCommand composes one or more ICommands.
/// </summary>
/// <remarks>
/// <para>
/// <see cref="CompositeCommand"/> allows you to register multiple commands and execute them all with a single call.
/// This is useful for scenarios where an action should trigger multiple operations across different parts of an application.
/// </para>
/// <para>
/// The composite command can optionally monitor the activity of its registered commands if they implement <see cref="IActiveAware"/>.
/// When monitoring is enabled, the composite command will only execute commands that are active.
/// </para>
/// <para>
/// The <see cref="CanExecute(object)"/> method returns <see langword="true"/> only if all registered commands can execute.
/// </para>
/// </remarks>
public class CompositeCommand : ICommand
{
private readonly List<ICommand> _registeredCommands = new();
Expand All @@ -21,6 +34,9 @@ public class CompositeCommand : ICommand
/// <summary>
/// Initializes a new instance of <see cref="CompositeCommand"/>.
/// </summary>
/// <remarks>
/// By default, the composite command will not monitor the activity of registered commands.
/// </remarks>
public CompositeCommand()
{
_onRegisteredCommandCanExecuteChangedHandler = new EventHandler(OnRegisteredCommandCanExecuteChanged);
Expand All @@ -30,7 +46,11 @@ public CompositeCommand()
/// <summary>
/// Initializes a new instance of <see cref="CompositeCommand"/>.
/// </summary>
/// <param name="monitorCommandActivity">Indicates when the command activity is going to be monitored.</param>
/// <param name="monitorCommandActivity">Indicates when the command activity is going to be monitored.
/// When <see langword="true"/>, the composite command will only execute registered commands that are active (if they implement <see cref="IActiveAware"/>).</param>
/// <remarks>
/// When activity monitoring is enabled, only commands that are active will be executed.
/// </remarks>
public CompositeCommand(bool monitorCommandActivity)
: this()
{
Expand All @@ -41,11 +61,19 @@ public CompositeCommand(bool monitorCommandActivity)
/// Adds a command to the collection and signs up for the <see cref="ICommand.CanExecuteChanged"/> event of it.
/// </summary>
/// <remarks>
/// <para>
/// 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.
/// </para>
/// <para>
/// The same command cannot be registered twice, and a composite command cannot be registered within itself.
/// </para>
/// </remarks>
/// <param name="command">The command to register.</param>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="command"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException">Thrown when attempting to register a composite command within itself.</exception>
/// <exception cref="InvalidOperationException">Thrown when the same command is registered twice.</exception>
public virtual void RegisterCommand(ICommand command)
{
if (command == null) throw new ArgumentNullException(nameof(command));
Expand Down Expand Up @@ -79,6 +107,7 @@ public virtual void RegisterCommand(ICommand command)
/// 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>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="command"/> is <see langword="null"/>.</exception>
public virtual void UnregisterCommand(ICommand command)
{
if (command == null) throw new ArgumentNullException(nameof(command));
Expand Down
42 changes: 39 additions & 3 deletions src/Prism.Core/Commands/DelegateCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,19 @@ namespace Prism.Commands
/// <summary>
/// An <see cref="ICommand"/> whose delegates do not take any parameters for <see cref="Execute()"/> and <see cref="CanExecute()"/>.
/// </summary>
/// <see cref="DelegateCommandBase"/>
/// <see cref="DelegateCommand{T}"/>
/// <remarks>
/// <para>
/// <see cref="DelegateCommand"/> is a command implementation that allows you to define the execute and canExecute logic
/// using delegates. Unlike UI-based commands, DelegateCommand does not accept parameters.
/// </para>
/// <para>
/// DelegateCommand supports fluent syntax through methods like <see cref="ObservesProperty{T}(Expression{Func{T}})"/>,
/// <see cref="ObservesCanExecute(Expression{Func{bool}})"/>, and <see cref="Catch(Action{Exception})"/> to make creating
/// and configuring commands easier.
/// </para>
/// </remarks>
/// <seealso cref="DelegateCommandBase"/>
/// <seealso cref="DelegateCommand{T}"/>
public class DelegateCommand : DelegateCommandBase
{
Action _executeMethod;
Expand All @@ -21,6 +32,7 @@ public class DelegateCommand : DelegateCommandBase
/// Creates a new instance of <see cref="DelegateCommand"/> with the <see cref="Action"/> to invoke on execution.
/// </summary>
/// <param name="executeMethod">The <see cref="Action"/> to invoke when <see cref="ICommand.Execute(object)"/> is called.</param>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="executeMethod"/> is <see langword="null"/>.</exception>
public DelegateCommand(Action executeMethod)
: this(executeMethod, () => true)
{
Expand All @@ -33,6 +45,10 @@ public DelegateCommand(Action executeMethod)
/// </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>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="executeMethod"/> or <paramref name="canExecuteMethod"/> is <see langword="null"/>.</exception>
/// <remarks>
/// Both delegates must be non-null. If either is null, an <see cref="ArgumentNullException"/> will be thrown.
/// </remarks>
public DelegateCommand(Action executeMethod, Func<bool> canExecuteMethod)
: base()
{
Expand All @@ -46,6 +62,10 @@ public DelegateCommand(Action executeMethod, Func<bool> canExecuteMethod)
///<summary>
/// Executes the command.
///</summary>
/// <remarks>
/// Calls the delegate registered during construction. If an exception occurs and has been registered
/// with <see cref="Catch(Action{Exception})"/>, it will be handled; otherwise, the exception is rethrown.
/// </remarks>
public void Execute()
{
try
Expand All @@ -65,6 +85,9 @@ public void Execute()
/// Determines if the command can be executed.
/// </summary>
/// <returns>Returns <see langword="true"/> if the command can execute,otherwise returns <see langword="false"/>.</returns>
/// <remarks>
/// Calls the canExecute delegate registered during construction. If an exception occurs, it will return <see langword="false"/>.
/// </remarks>
public bool CanExecute()
{
try
Expand Down Expand Up @@ -94,7 +117,7 @@ protected override void Execute(object? parameter)
/// <summary>
/// Handle the internal invocation of <see cref="ICommand.CanExecute(object)"/>
/// </summary>
/// <param name="parameter"></param>
/// <param name="parameter">Command Parameter (ignored by this implementation)</param>
/// <returns><see langword="true"/> if the Command Can Execute, otherwise <see langword="false" /></returns>
protected override bool CanExecute(object? parameter)
{
Expand All @@ -107,6 +130,9 @@ protected override bool CanExecute(object? parameter)
/// <typeparam name="T">The object type containing the property specified in the expression.</typeparam>
/// <param name="propertyExpression">The property expression. Example: ObservesProperty(() => PropertyName).</param>
/// <returns>The current instance of DelegateCommand</returns>
/// <remarks>
/// This method enables automatic notification of command execution state changes when observed properties change.
/// </remarks>
public DelegateCommand ObservesProperty<T>(Expression<Func<T>> propertyExpression)
{
ObservesPropertyInternal(propertyExpression);
Expand All @@ -118,6 +144,10 @@ public DelegateCommand ObservesProperty<T>(Expression<Func<T>> propertyExpressio
/// </summary>
/// <param name="canExecuteExpression">The property expression. Example: ObservesCanExecute(() => PropertyName).</param>
/// <returns>The current instance of DelegateCommand</returns>
/// <remarks>
/// This method replaces the canExecute delegate provided during construction with the compiled expression,
/// and automatically raises CanExecuteChanged when the observed property changes.
/// </remarks>
public DelegateCommand ObservesCanExecute(Expression<Func<bool>> canExecuteExpression)
{
_canExecuteMethod = canExecuteExpression.Compile();
Expand All @@ -130,6 +160,9 @@ public DelegateCommand ObservesCanExecute(Expression<Func<bool>> canExecuteExpre
/// </summary>
/// <param name="catch">The Callback</param>
/// <returns>The current instance of <see cref="DelegateCommand"/></returns>
/// <remarks>
/// The exception handler will be invoked only for exceptions of type <see cref="Exception"/>.
/// </remarks>
public DelegateCommand Catch(Action<Exception> @catch)
{
ExceptionHandler.Register<Exception>(@catch);
Expand All @@ -141,6 +174,9 @@ public DelegateCommand Catch(Action<Exception> @catch)
/// </summary>
/// <param name="catch">The Callback</param>
/// <returns>The current instance of <see cref="DelegateCommand"/></returns>
/// <remarks>
/// The exception handler will be invoked with both the exception and the command parameter.
/// </remarks>
public DelegateCommand Catch(Action<Exception, object> @catch)
{
ExceptionHandler.Register<Exception>(@catch);
Expand Down