0

I have a class called RelayCommand that inherits the ICommand interface defined like this:

/// <summary>
/// Base command that executes an <see cref="Action"/>.
/// </summary>
public class RelayCommand : ICommand
{
    #region Members

    // The Action to run.
    private Action mAction;

    #endregion

    #region Events

    /// <summary>
    /// Event fired when the value of <see cref="CanExecute(object)"/> changes.
    /// </summary>
    public event EventHandler CanExecuteChanged = (sender, e) => { };

    #endregion

    #region Constructor

    /// <summary>
    /// Default <see cref="RelayCommand"/> constructor.
    /// </summary>
    public RelayCommand(Action action)
    {
        mAction = action;
    }

    #endregion

    #region Command Methods

    /// <summary>
    /// A <see cref="RelayCommand"/> can always be executed.
    /// </summary>
    /// <param name="parameter"></param>
    /// <returns></returns>
    public bool CanExecute(object parameter)
    {
        return true;

    }

    /// <summary>
    /// Executes the <see cref="Action"/> of the command.
    /// </summary>
    /// <param name="parameter"></param>
    public void Execute(object parameter)
    {
        mAction();
    }

    #endregion 
}

I use this class to execute commands throughout the application and sometimes I need to perform Actions passing a parameter in the XAML. The problem is that this parameter passing is optional but I can't find a solution that fits with what I'm doing. For example, I have these three commands that basically do the same thing. I set the PriorityLevel (enum value) to an object through the UI and, depending on which control is clicked, I execute one of the following three methods:

    /// <summary>
    /// What to do when the priority of the task is set to <see cref="PriorityLevel.Low"/>
    /// </summary>
    private void OnSetLowPriorityCommand()
    {
        Priority = PriorityLevel.Low;
        PriorityGridWidth = 10;
        PriorityFlagOpacity = 0;
        mIsPriorityChanged = true;
    }

    /// <summary>
    /// What to do when the priority of the task is set to <see cref="PriorityLevel.Medium"/>
    /// </summary>
    private void OnSetMediumPriorityCommand()
    {
        Priority = PriorityLevel.Medium;
        PriorityGridWidth = 10;
        PriorityFlagOpacity = 0;
        mIsPriorityChanged = true;
    }

    /// <summary>
    /// What to do when the priority of the task is set to <see cref="PriorityLevel.High"/>
    /// </summary>
    private void OnSetHighPriorityCommand()
    {
        Priority = PriorityLevel.High;
        PriorityGridWidth = 10;
        PriorityFlagOpacity = 0;
        mIsPriorityChanged = true;
    }

As you can see, apart of the first line of the methods, the rest is the same, but I think that it would be better if I keep only one method called, for example, SetPriorityCommand that, through a switch or whatever, sets the right PriorityLevel value. I then consume the Actions like this:

    SetLowPriorityCommand = new RelayCommand(OnSetLowPriorityCommand);

Anyways, in other cases I execute an Action without the need of passing a parameter.

Finally, in the XAML I need to execute commands like this:

    <Border.InputBindings>
        <MouseBinding MouseAction="LeftClick"
                      Command="{Binding SetLowPriorityCommand}"
                      CommandParameter="{Binding Source={x:Static core:PriorityLevel.Low}}"/> <!-- NOT ALWAYS NECESSARY -->
    </Border.InputBindings>

I found plenty of answers about this but none of them seemed to make the use of parameters optional.

How can I adjust the RelayCommand class to fit my needs?

Thank you in advance for the help.

Belfed
  • 165
  • 1
  • 13
  • 1
    You can have two `RelayCommand` one `RelayCommand` that takes an `Action` and then `RelayCommand` that takes and `Action` that you can send a parameter to. Or just take an `Action` and ignore the parameter when you don't need it. – JSteward Jan 08 '19 at 22:47
  • 1
    Or only `RelayCommand`, and when no parameter is required, use `RelayCommand` and ignore the parameter. – Clemens Jan 08 '19 at 22:49

1 Answers1

1

The simplest change to your code would be just take an object parameter and cast that when you call your method. Otherwise you can search the web and find plenty of implementations of RelayCommand<T> but here's an example of just using an object parameter both when you use the argument and when you don't.

public class RelayCommand : ICommand
{
    private Action<object> mAction;

    public event EventHandler CanExecuteChanged = (sender, e) => { };

    public RelayCommand(Action<object> action)
    {
        mAction = action;
    }

    public bool CanExecute(object parameter)
    {
        return true;

    }

    public void Execute(object parameter)
    {
        mAction(parameter);
    }
}

public class ViewModel
{

    //ignore the argument
    private RelayCommand SetLowPriorityCommand { get => new RelayCommand(_ => SetLowPriority()); }

    //cast and use the argument
    private RelayCommand SetPriority { get => new RelayCommand(priority => SetPriority((int)priority)); }


    private void SetLowPriority()
    {
        //....///
    }

    private void SetPriority(int priority)
    {
        //...//
    }
}
JSteward
  • 6,833
  • 2
  • 21
  • 30
  • Thank you, it is what I was looking for. I've never seen the `_ =>` that allows me to ignore the parameters. – Belfed Jan 09 '19 at 19:24