CommunityToolkit / Maui

The .NET MAUI Community Toolkit is a community-created library that contains .NET MAUI Extensions, Advanced UI/UX Controls, and Behaviors to help make your life as a .NET MAUI developer easier
https://learn.microsoft.com/dotnet/communitytoolkit/maui
MIT License
2.27k stars 401 forks source link

[Proposal] CommandFactory #97

Closed brminnick closed 3 years ago

brminnick commented 3 years ago

CommandFactory

Summary

The CommandFactory class provides a unified approach to creating new Command, AsyncCommand, and AsyncValueCommand objects

Detailed Design

CommandFactory.Comand.shared.cs

public static partial class CommandFactory
{
  public static Command Create(Action execute) => new Command(execute);

  public static Command Create(Action execute, Func<bool> canExecute) => new Command(execute, canExecute);

  public static Command Create(Action<object> execute) => new Command(execute);

  public static Command Create(Action<object> execute, Func<object, bool> canExecute) => new Command(execute, canExecute);

  public static Command<T> Create<T>(Action<T> execute) => new Command<T>(execute);

  public static Command<T> Create<T>(Action<T> execute, Func<T, bool> canExecute) => new Command<T>(execute, canExecute);
}

CommandFactory.IAsyncCommand.shared.cs

public static partial class CommandFactory
{
  public static IAsyncCommand Create(
    Func<Task> execute,
    Func<object?, bool>? canExecute = null,
    Action<Exception>? onException = null,
    bool continueOnCapturedContext = false,
    bool allowsMultipleExecutions = true) => new AsyncCommand(execute, canExecute, onException, continueOnCapturedContext, allowsMultipleExecutions);

  public static IAsyncCommand Create(
    Func<Task> execute,
    Func<bool> canExecute,
    Action<Exception>? onException = null,
    bool continueOnCapturedContext = false,
    bool allowsMultipleExecutions = true) => new AsyncCommand(execute, canExecute, onException, continueOnCapturedContext, allowsMultipleExecutions);

  public static IAsyncCommand<TExecute> Create<TExecute>(
    Func<TExecute?, Task> execute,
    Func<object?, bool>? canExecute = null,
    Action<Exception>? onException = null,
    bool continueOnCapturedContext = false,
    bool allowsMultipleExecutions = true) => new AsyncCommand<TExecute>(execute, canExecute, onException, continueOnCapturedContext, allowsMultipleExecutions);

  public static IAsyncCommand<TExecute> Create<TExecute>(
    Func<TExecute?, Task> execute,
    Func<bool> canExecute,
    Action<Exception>? onException = null,
    bool continueOnCapturedContext = false,
    bool allowsMultipleExecutions = true) => new AsyncCommand<TExecute>(execute, canExecute, onException, continueOnCapturedContext, allowsMultipleExecutions);

  public static IAsyncCommand<TExecute, TCanExecute> Create<TExecute, TCanExecute>(
    Func<TExecute?, Task> execute,
    Func<TCanExecute?, bool>? canExecute = null,
    Action<Exception>? onException = null,
    bool continueOnCapturedContext = false,
    bool allowsMultipleExecutions = true) => new AsyncCommand<TExecute, TCanExecute>(execute, canExecute, onException, continueOnCapturedContext, allowsMultipleExecutions);

  public static IAsyncCommand Create(Func<Task> execute)
  {
    Func<object?, bool>? canExecute = null;
    return Create(execute, canExecute, null, false, true);
  }

  public static IAsyncCommand Create(Func<Task> execute, Func<object?, bool> canExecute) => Create(execute, canExecute, null, false, true);

  public static IAsyncCommand Create(Func<Task> execute, Func<bool> canExecute) => Create(execute, canExecute, null, false, true);

  public static IAsyncCommand<TExecute> Create<TExecute>(Func<TExecute?, Task> execute)
  {
    Func<object?, bool>? canExecute = null;
    return Create(execute, canExecute, null, false, true);
  }

  public static IAsyncCommand<TExecute> Create<TExecute>(Func<TExecute?, Task> execute, Func<object?, bool> canExecute) => Create(execute, canExecute, null, false, true);

  public static IAsyncCommand<TExecute> Create<TExecute>(Func<TExecute?, Task> execute, Func<bool> canExecute) => Create(execute, canExecute, null, false, true);

  public static IAsyncCommand<TExecute, TCanExecute> Create<TExecute, TCanExecute>(Func<TExecute?, Task> execute, Func<TCanExecute?, bool> canExecute) => Create(execute, canExecute, null, false, true);
}

CommandFactory.IAsyncValueCommand.shared.cs

public static partial class CommandFactory
{
  public static IAsyncValueCommand Create(
      Func<ValueTask> execute,
      Func<object?, bool>? canExecute = null,
      Action<Exception>? onException = null,
      bool continueOnCapturedContext = false,
      bool allowsMultipleExecutions = true) => new AsyncValueCommand(execute, canExecute, onException, continueOnCapturedContext, allowsMultipleExecutions);

  public static IAsyncValueCommand Create(
      Func<ValueTask> execute,
      Func<bool> canExecute,
      Action<Exception>? onException = null,
      bool continueOnCapturedContext = false,
      bool allowsMultipleExecutions = true) => new AsyncValueCommand(execute, canExecute, onException, continueOnCapturedContext, allowsMultipleExecutions);

  public static IAsyncValueCommand<TExecute> Create<TExecute>(
      Func<TExecute?, ValueTask> execute,
      Func<object?, bool>? canExecute = null,
      Action<Exception>? onException = null,
      bool continueOnCapturedContext = false,
      bool allowsMultipleExecutions = true) => new AsyncValueCommand<TExecute>(execute, canExecute, onException, continueOnCapturedContext, allowsMultipleExecutions);

  public static IAsyncValueCommand<TExecute> Create<TExecute>(
      Func<TExecute?, ValueTask> execute,
      Func<bool> canExecute,
      Action<Exception>? onException = null,
      bool continueOnCapturedContext = false,
      bool allowsMultipleExecutions = true) => new AsyncValueCommand<TExecute>(execute, canExecute, onException, continueOnCapturedContext, allowsMultipleExecutions);

  public static IAsyncValueCommand<TExecute, TCanExecute> Create<TExecute, TCanExecute>(
      Func<TExecute?, ValueTask> execute,
      Func<TCanExecute?, bool>? canExecute = null,
      Action<Exception>? onException = null,
      bool continueOnCapturedContext = false,
      bool allowsMultipleExecutions = true) => new AsyncValueCommand<TExecute, TCanExecute>(execute, canExecute, onException, continueOnCapturedContext, allowsMultipleExecutions);
}

Usage Syntax

XAML Usage

N/A

C# Usage

Command command = CommandFactory.Create(() => Debug.WriteLine("Command executed"));
Command<string> commandWithParameter = CommandFactory.Create<string>(p => Debug.WriteLine("Command executed: {0}", p));

IAsyncCommand asyncCommand = CommandFactory.Create(ExecuteCommandAsync)
IAsyncCommand<int> asyncCommandWithParameter = CommandFactory.Create<int>(ExecuteCommandAsync)

async Task ExecuteCommandAsync()
{
    // ...
}

async Task ExecuteCommandAsync(int commandParameter)
{
    // ...
}

Drawbacks

CommandFactory augments both AsyncCommand and AsyncValueCommand, both of which are not being included in the .NET MAUI Toolkit:

Alternatives

MVVM Toolkit AsyncRelayCommand

https://docs.microsoft.com/en-us/windows/communitytoolkit/mvvm/asyncrelaycommand

brminnick commented 3 years ago

Recommend adding CommandFactory to the MVVM Toolkit to augment RelayCommand

https://docs.microsoft.com/en-us/windows/communitytoolkit/mvvm/asyncrelaycommand