Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using multiple Actions in Action<object> list

I'm learning a bit more about using the ICommand interface and how to execute commands through the RelayCommand class I created.

Firstly, I did some research on what I am trying to achieve, but I can't find anything to help me or I do not really know how to search for what I am trying to do. Also, my knowledge of C# theory isn't really good so that's also not in my favour.

Basically I'm trying to send multiple actions through to my RelayCommand constructor and then iterate through all of the actions in the List<Action<object>> collection and execute them one by one. Here is my code:

RelayCommand class constructor

public RelayCommand(Predicate<object> canExecute, List<Action<object>> actions)
{
    _canExecute = canExecute;

    foreach (var action in actions)
        _execute = action;
}

And then my command property inside my ViewModel

private ICommand _updateExternals;
public ICommand UpdateExternals
{
    get
    {
        if (_updateExternals == null)
        {
            _updateExternals = new RelayCommand(
                x => CanExecute,
                Updater.UpdateExternals(this));//Here is where I am stuck
        }
        return _updateExternals;
    }
}

I know, from other examples, that if I only need to pass through a single Action instead of a list of Actions that the code would look like the following: x => Updater.UpdateExternals(this)); and it would work, but as soon as I have a list of Actions as a parameter I get stuck.

So, if you haven't already guessed, my question is: How can I send through multiple Actions to my RelayCommand class?

Any help in the right direction would be appreciated, thanks!

EDIT: Here is my full RelayCommand class to give more of an idea of what I am doing.

public class RelayCommand : ICommand
{
    private Predicate<object> _canExecute;
    private Action<object> _execute;

    public RelayCommand(Predicate<object> canExecute, List<Action<object>> actions)
    {
        _canExecute = canExecute;
        foreach (var action in actions)
            _execute = action;
    }

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public bool CanExecute(object parameter)
    {
        return _canExecute(parameter);
    }

    public void Execute(object parameter)
    {
        _execute(parameter);
    }
}
like image 488
CareTaker22 Avatar asked Jan 29 '23 07:01

CareTaker22


2 Answers

To solve your problem, use params in constructor:

public class RelayCommand : ICommand {
    private Predicate<object> _canExecute;
    private Action<object>[] _execute;

    public RelayCommand(Predicate<object> canExecute, params Action<object>[] actions) {
        _canExecute = canExecute;
        _execute = actions;
    }

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public bool CanExecute(object parameter) {
        return _canExecute(parameter);
    }

    public void Execute(object parameter) {
        foreach (var action in _execute)
            action(parameter);
    }
}

Then you can create such command as usual:

_updateExternals = new RelayCommand(
    x => CanExecute,
    x => Updater.UpdateExternals(this));//Here is where I am stuck

When you need to pass multiple actions:

_updateExternals = new RelayCommand(
    x => CanExecute,
    x => Updater.UpdateExternals(this),
    x => Updater.DoSomethingElse(this));
like image 146
Evk Avatar answered Feb 02 '23 11:02

Evk


Your class needs to have a list of the actions you want to execute, so add that as a field in your class and then set it in the constructor. Then in your Execute method (interface implementation) you just iterate over the actions and call them one after another.

public class RelayCommand : ICommand
{
    private Predicate<object> _canExecute;
    private List<Action<object>> _executes;

    public RelayCommand(Predicate<object> canExecute, List<Action<object>> actions)
    {
        _canExecute = canExecute;
        _executes = actions;
    }

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public bool CanExecute(object parameter)
    {
        return _canExecute(parameter);
    }

    public void Execute(object parameter)
    {
        foreach(e in _executes )
        {            
           e(parameter);
        }
    }
}

What you original code was doing was assigning the single action reference over and over with each of the actions from the constructor parameter so your class would only have executed the last action in the list when Execute is called.

Remember an Action and Action<T> is more or less just a reference to some method, named or anonymous (lambda).

like image 34
Sindri Jóelsson Avatar answered Feb 02 '23 11:02

Sindri Jóelsson