web-dev-qa-db-fra.com

Implémentation ICommand MVVM

Donc, dans cette implémentation MVVM particulière que je fais, j'ai besoin de plusieurs commandes. J'étais vraiment fatigué d'implémenter les classes ICommand une par une, alors j'ai trouvé une solution, mais je ne sais pas à quel point c'est bon, donc la contribution de tout expert WPF ici sera grandement appréciée. Et si vous pouviez offrir une meilleure solution, encore mieux.

Ce que j'ai fait est une seule classe ICommand et deux délégués qui prennent un objet comme paramètre, un délégué est nul (pour OnExecute), l'autre bool (pour OnCanExecute). Donc, dans le constructeur de mon ICommand (qui est appelé par la classe ViewModel) j'envoie les deux méthodes, et sur chaque méthode ICommand j'invoque les méthodes des délégués.

Cela fonctionne vraiment bien, mais je ne sais pas si c'est une mauvaise façon de le faire, ou s'il y a une meilleure façon. Ci-dessous le code complet, toute entrée sera grandement appréciée, même négative, mais soyez constructif.

ViewModel:

public class TestViewModel : DependencyObject
{
    public ICommand Command1 { get; set; }
    public ICommand Command2 { get; set; }
    public ICommand Command3 { get; set; }

    public TestViewModel()
    {
        this.Command1 = new TestCommand(ExecuteCommand1, CanExecuteCommand1);
        this.Command2 = new TestCommand(ExecuteCommand2, CanExecuteCommand2);
        this.Command3 = new TestCommand(ExecuteCommand3, CanExecuteCommand3);
    }

    public bool CanExecuteCommand1(object parameter)
    {
        return true;
    }

    public void ExecuteCommand1(object parameter)
    {
        MessageBox.Show("Executing command 1");
    }

    public bool CanExecuteCommand2(object parameter)
    {
        return true;
    }

    public void ExecuteCommand2(object parameter)
    {
        MessageBox.Show("Executing command 2");
    }

    public bool CanExecuteCommand3(object parameter)
    {
        return true;
    }

    public void ExecuteCommand3(object parameter)
    {
        MessageBox.Show("Executing command 3");
    }
}

Je commande:

public class TestCommand : ICommand
{
    public delegate void ICommandOnExecute(object parameter);
    public delegate bool ICommandOnCanExecute(object parameter);

    private ICommandOnExecute _execute;
    private ICommandOnCanExecute _canExecute;

    public TestCommand(ICommandOnExecute onExecuteMethod, ICommandOnCanExecute onCanExecuteMethod)
    {
        _execute = onExecuteMethod;
        _canExecute = onCanExecuteMethod;
    }

    #region ICommand Members

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

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

    public void Execute(object parameter)
    {
        _execute.Invoke(parameter);
    }

    #endregion
}
53
Carlo

Ceci est presque identique à la façon dont Karl Shifflet l'a démontré a RelayCommand, où Execute déclenche un Action<T> Prédéterminé. Une solution de premier ordre, si vous me demandez.

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

    public RelayCommand(Predicate<object> canExecute, Action<object> execute)
    {
        this._canExecute = canExecute;
        this._execute = execute;
    }

    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);
    }
}

Cela pourrait alors être utilisé comme ...

public class MyViewModel
{
    private ICommand _doSomething;
    public ICommand DoSomethingCommand
    {
        get
        {
            if (_doSomething == null)
            {
                _doSomething = new RelayCommand(
                    p => this.CanDoSomething,
                    p => this.DoSomeImportantMethod());
            }
            return _doSomething;
        }
    }
}

Lire la suite:
Josh Smith (introducteur de RelayCommand): Modèles - Applications WPF avec le modèle de conception MVVM

67
Jarrett Meyer

J'ai écrit ceci article à propos de l'interface ICommand.

L'idée - créer une commande universelle qui prend deux délégués: l'un est appelé lorsque ICommand.Execute (object param) est appelé, le second vérifie si vous pouvez exécuter la commande (ICommand.CanExecute (object param)).

Requiert la méthode pour basculer l'événement CanExecuteChanged. Il est appelé à partir des éléments de l'interface utilisateur pour commuter la commande state CanExecute().

public class ModelCommand : ICommand
{
    #region Constructors

    public ModelCommand(Action<object> execute)
        : this(execute, null) { }

    public ModelCommand(Action<object> execute, Predicate<object> canExecute)
    {
        _execute = execute;
        _canExecute = canExecute;
    }

    #endregion

    #region ICommand Members

    public event EventHandler CanExecuteChanged;

    public bool CanExecute(object parameter)
    {
        return _canExecute != null ? _canExecute(parameter) : true;
    }

    public void Execute(object parameter)
    {
        if (_execute != null)
            _execute(parameter);
    }

    public void OnCanExecuteChanged()
    {
        CanExecuteChanged(this, EventArgs.Empty);
    }

    #endregion

    private readonly Action<object> _execute = null;
    private readonly Predicate<object> _canExecute = null;
}
13
kurik

Je viens de créer un peu exemple montrant comment implémenter les commandes par convention sur le style de configuration. Cependant, cela nécessite que Reflection.Emit () soit disponible. Le code de support peut sembler un peu bizarre mais une fois écrit, il peut être utilisé plusieurs fois.

Taquin:

public class SampleViewModel: BaseViewModelStub
{
    public string Name { get; set; }

    [UiCommand]
    public void HelloWorld()
    {
        MessageBox.Show("Hello World!");
    }

    [UiCommand]
    public void Print()
    {
        MessageBox.Show(String.Concat("Hello, ", Name, "!"), "SampleViewModel");
    }

    public bool CanPrint()
    {
        return !String.IsNullOrEmpty(Name);
    }
}

}

[~ # ~] mise à jour [~ # ~] : il semble maintenant exister des bibliothèques comme http: //www.codeproject. com/Articles/101881/Executing-Command-Logic-in-a-View-Model qui résout le problème du code passe-partout ICommand.

10
Boris Treukhov

@Carlo J'aime vraiment votre implémentation de cela, mais je voulais partager ma version et comment l'utiliser dans mon ViewModel

Premier implémenter ICommand

public class Command : ICommand
{
    public delegate void ICommandOnExecute();
    public delegate bool ICommandOnCanExecute();

    private ICommandOnExecute _execute;
    private ICommandOnCanExecute _canExecute;

    public Command(ICommandOnExecute onExecuteMethod, ICommandOnCanExecute onCanExecuteMethod = null)
    {
        _execute = onExecuteMethod;
        _canExecute = onCanExecuteMethod;
    }

    #region ICommand Members

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

    public bool CanExecute(object parameter)
    {
        return _canExecute?.Invoke() ?? true;
    }

    public void Execute(object parameter)
    {
        _execute?.Invoke();
    }

    #endregion
}

Remarquez que j'ai supprimé le paramètre de ICommandOnExecute et ICommandOnCanExecute et ajouté un null au constructeur

Puis à utiliser dans le ViewModel

public Command CommandToRun_WithCheck
{
    get
    {
        return new Command(() =>
        {
            // Code to run
        }, () =>
        {
            // Code to check to see if we can run 
            // Return true or false
        });
    }
}

public Command CommandToRun_NoCheck
{
    get
    {
        return new Command(() =>
        {
            // Code to run
        });
    }
}

Je trouve juste cette façon plus propre car je n'ai pas besoin d'attribuer de variables, puis d'instancier, tout cela en une seule fois.

2
jack_tux