Skip to content

MVVM Toolkit - Make Command classes open for extension #3610

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
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
7 changes: 5 additions & 2 deletions Microsoft.Toolkit.Mvvm/Input/AsyncRelayCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ namespace Microsoft.Toolkit.Mvvm.Input
/// action, and providing an <see cref="ExecutionTask"/> property that notifies changes when
/// <see cref="ExecuteAsync"/> is invoked and when the returned <see cref="Task"/> completes.
/// </summary>
public sealed class AsyncRelayCommand : ObservableObject, IAsyncRelayCommand
public class AsyncRelayCommand : ObservableObject, IAsyncRelayCommand
{
/// <summary>
/// The cached <see cref="PropertyChangedEventArgs"/> for <see cref="CanBeCanceled"/>.
Expand Down Expand Up @@ -57,7 +57,7 @@ public sealed class AsyncRelayCommand : ObservableObject, IAsyncRelayCommand
private CancellationTokenSource? cancellationTokenSource;

/// <inheritdoc/>
public event EventHandler? CanExecuteChanged;
public virtual event EventHandler? CanExecuteChanged;

/// <summary>
/// Initializes a new instance of the <see cref="AsyncRelayCommand"/> class that can always execute.
Expand Down Expand Up @@ -121,6 +121,9 @@ private set
}
}

/// <inheritdoc/>
public bool CanAlwaysExecute => this.canExecute is null;

/// <inheritdoc/>
public bool CanBeCanceled => !(this.cancelableExecute is null) && IsRunning;

Expand Down
7 changes: 5 additions & 2 deletions Microsoft.Toolkit.Mvvm/Input/AsyncRelayCommand{T}.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace Microsoft.Toolkit.Mvvm.Input
/// A generic command that provides a more specific version of <see cref="AsyncRelayCommand"/>.
/// </summary>
/// <typeparam name="T">The type of parameter being passed as input to the callbacks.</typeparam>
public sealed class AsyncRelayCommand<T> : ObservableObject, IAsyncRelayCommand<T>
public class AsyncRelayCommand<T> : ObservableObject, IAsyncRelayCommand<T>
{
/// <summary>
/// The <see cref="Func{TResult}"/> to invoke when <see cref="Execute(T)"/> is used.
Expand All @@ -37,7 +37,7 @@ public sealed class AsyncRelayCommand<T> : ObservableObject, IAsyncRelayCommand<
private CancellationTokenSource? cancellationTokenSource;

/// <inheritdoc/>
public event EventHandler? CanExecuteChanged;
public virtual event EventHandler? CanExecuteChanged;

/// <summary>
/// Initializes a new instance of the <see cref="AsyncRelayCommand{T}"/> class that can always execute.
Expand Down Expand Up @@ -105,6 +105,9 @@ private set
}
}

/// <inheritdoc/>
public bool CanAlwaysExecute => this.canExecute is null;

/// <inheritdoc/>
public bool CanBeCanceled => !(this.cancelableExecute is null) && IsRunning;

Expand Down
5 changes: 5 additions & 0 deletions Microsoft.Toolkit.Mvvm/Input/Interfaces/IRelayCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,10 @@ public interface IRelayCommand : ICommand
/// Notifies that the <see cref="ICommand.CanExecute"/> property has changed.
/// </summary>
void NotifyCanExecuteChanged();

/// <summary>
/// Gets a value indicating whether this command can always be executed. True if the command was created without execution status logic.
/// </summary>
bool CanAlwaysExecute { get; }
Comment on lines 16 to +23
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Notifies that the <see cref="ICommand.CanExecute"/> property has changed.
/// </summary>
void NotifyCanExecuteChanged();
/// <summary>
/// Gets a value indicating whether this command can always be executed. True if the command was created without execution status logic.
/// </summary>
bool CanAlwaysExecute { get; }
/// Gets a value indicating whether this command can always be executed. True if the command was created without execution status logic.
/// </summary>
bool CanAlwaysExecute { get; }
/// <summary>
/// Notifies that the <see cref="ICommand.CanExecute"/> property has changed.
/// </summary>
void NotifyCanExecuteChanged();

Put property above method? I know StyleCop enforces this for classes, but I guess not for interfaces?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, you're absolutely right. Should be like suggested by you if we would merge it. Thanks @michael-hawker

}
}
7 changes: 5 additions & 2 deletions Microsoft.Toolkit.Mvvm/Input/RelayCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ namespace Microsoft.Toolkit.Mvvm.Input
/// method is <see langword="true"/>. This type does not allow you to accept command parameters
/// in the <see cref="Execute"/> and <see cref="CanExecute"/> callback methods.
/// </summary>
public sealed class RelayCommand : IRelayCommand
public class RelayCommand : IRelayCommand
{
/// <summary>
/// The <see cref="Action"/> to invoke when <see cref="Execute"/> is used.
Expand All @@ -31,7 +31,7 @@ public sealed class RelayCommand : IRelayCommand
private readonly Func<bool>? canExecute;

/// <inheritdoc/>
public event EventHandler? CanExecuteChanged;
public virtual event EventHandler? CanExecuteChanged;

/// <summary>
/// Initializes a new instance of the <see cref="RelayCommand"/> class that can always execute.
Expand All @@ -53,6 +53,9 @@ public RelayCommand(Action execute, Func<bool> canExecute)
this.canExecute = canExecute;
}

/// <inheritdoc/>
public bool CanAlwaysExecute => this.canExecute is null;

/// <inheritdoc/>
public void NotifyCanExecuteChanged()
{
Expand Down
7 changes: 5 additions & 2 deletions Microsoft.Toolkit.Mvvm/Input/RelayCommand{T}.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ namespace Microsoft.Toolkit.Mvvm.Input
/// in the <see cref="Execute(T)"/> and <see cref="CanExecute(T)"/> callback methods.
/// </summary>
/// <typeparam name="T">The type of parameter being passed as input to the callbacks.</typeparam>
public sealed class RelayCommand<T> : IRelayCommand<T>
public class RelayCommand<T> : IRelayCommand<T>
{
/// <summary>
/// The <see cref="Action"/> to invoke when <see cref="Execute(T)"/> is used.
Expand All @@ -32,7 +32,7 @@ public sealed class RelayCommand<T> : IRelayCommand<T>
private readonly Func<T, bool>? canExecute;

/// <inheritdoc/>
public event EventHandler? CanExecuteChanged;
public virtual event EventHandler? CanExecuteChanged;

/// <summary>
/// Initializes a new instance of the <see cref="RelayCommand{T}"/> class that can always execute.
Expand Down Expand Up @@ -60,6 +60,9 @@ public RelayCommand(Action<T> execute, Func<T, bool> canExecute)
this.canExecute = canExecute;
}

/// <inheritdoc/>
public bool CanAlwaysExecute => this.canExecute is null;

/// <inheritdoc/>
public void NotifyCanExecuteChanged()
{
Expand Down