61

I've been programming a lot in WPF lately but my View and ViewModel are not separate at this point. Well, it's partially. All my bindings concerned to text in text boxes, content for labels, lists in datagrids, ... are done by regular properties with a NotifyPropertyChanged event in them.

All my events for handling button clicks or text changed stuff is done by linking the events. Now, I wanted to start working with commands and found this article: http://www.codeproject.com/Articles/126249/MVVM-Pattern-in-WPF-A-Simple-Tutorial-for-Absolute. It has an explanation of how to set up MVVM but I'm confused with the RelayCommand.

What job does it do? Is it useable for all commands in my form? How do I make the button disable when (a) certain text box(es) are not filled in?


EDIT 1:

A good explanation to "Is it useable for all commands in my form?" is answered here: https://stackoverflow.com/a/22286816/3357699

Here is the code I have so far: https://stackoverflow.com/a/22289358/3357699

Community
  • 1
  • 1
Krowi
  • 1,565
  • 3
  • 20
  • 40
  • `Is it useable for all commands in my form?` - What commands you are referring to here? – Rohit Vats Mar 09 '14 at 18:50
  • the `Click` and `TextChanged` – Krowi Mar 09 '14 at 18:55
  • For `Click` you can directly bind `Command` DP of button to some `ICommand` in your ViewModel. But to bind `TextChanged` you need to use interactivity triggers to bind `ICommand` in your ViewModel. – Rohit Vats Mar 09 '14 at 18:56
  • 2
    RelayCommand implement ICommand and lets you define an action that shall be used when Execute called. That is how you end up using one class for all commands and you just change the action instead of creating each class for each command implementing ICommand. Commanding in wpf to work needs ICommand. – dev hedgehog Mar 09 '14 at 19:42
  • I tried something out myself and posted what I got. I got it working but still have 2 more questions who I like to be answered here instead of making a new question. – Krowi Mar 09 '14 at 22:59
  • @devhedgehog has the best explanation for why such a class is necessary (the first question in the OP). – John Jesus Mar 20 '14 at 21:19

2 Answers2

96

Commands are used to separate the semantics and the object that invokes a command from the logic that executes the command i.e. it separates UI component from the logic that needs to be executed on command invocation. So, that you can test business logic separately using test cases and also your UI code is loosely coupled to business logic.

Now, that being said let's pick your questions one by one:

What job does it do?

I have added the details above. Hope it clears the usage of commands.


Is it usable for all commands in my form?

Some controls exposed Command DependencyProperty like Button, MenuItem etc. which have some default event registered to it. For Button it's Click event. So, if you bind ICommand declared in ViewModel with Button's Command DP, it will be invoked whenever button is clicked on.

For other events, you can bind using Interactivity triggers. Refer to the sample here how to use them to bind to ICommand in ViewModel.


How do I make the button disable when (a) certain text box(es) are not filled in?

The link you posted doesn't provide complete implementation of RelayCommand. It lacks the overloaded constructor to set CanExecute predicate which plays a key role in enabling/disabling the UI control to which your command is bound to.

Bound TextBoxes with some properties in ViewModel and in CanExecute delegate returns false if any of the bound properties are null or empty which automatically disabled the control to which command is bound to.


Full implementation of RelayCommand:

public class RelayCommand<T> : ICommand
{
    #region Fields

    readonly Action<T> _execute = null;
    readonly Predicate<T> _canExecute = null;

    #endregion

    #region Constructors

    /// <summary>
    /// Initializes a new instance of <see cref="DelegateCommand{T}"/>.
    /// </summary>
    /// <param name="execute">Delegate to execute when Execute is called on the command.  This can be null to just hook up a CanExecute delegate.</param>
    /// <remarks><seealso cref="CanExecute"/> will always return true.</remarks>
    public RelayCommand(Action<T> execute)
        : this(execute, null)
    {
    }

    /// <summary>
    /// Creates a new command.
    /// </summary>
    /// <param name="execute">The execution logic.</param>
    /// <param name="canExecute">The execution status logic.</param>
    public RelayCommand(Action<T> execute, Predicate<T> canExecute)
    {
        if (execute == null)
            throw new ArgumentNullException("execute");

        _execute = execute;
        _canExecute = canExecute;
    }

    #endregion

    #region ICommand Members

    ///<summary>
    ///Defines the method that determines whether the command can execute in its current state.
    ///</summary>
    ///<param name="parameter">Data used by the command.  If the command does not require data to be passed, this object can be set to null.</param>
    ///<returns>
    ///true if this command can be executed; otherwise, false.
    ///</returns>
    public bool CanExecute(object parameter)
    {
        return _canExecute == null || _canExecute((T)parameter);
    }

    ///<summary>
    ///Occurs when changes occur that affect whether or not the command should execute.
    ///</summary>
    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    ///<summary>
    ///Defines the method to be called when the command is invoked.
    ///</summary>
    ///<param name="parameter">Data used by the command. If the command does not require data to be passed, this object can be set to <see langword="null" />.</param>
    public void Execute(object parameter)
    {
        _execute((T)parameter);
    }

    #endregion
}
Neuron
  • 5,141
  • 5
  • 38
  • 59
Rohit Vats
  • 79,502
  • 12
  • 161
  • 185
  • 2
    This is a wonderful explanation and example how it is done, but I'm afraid a little confused still. I used to program in C# but had to switch to VB.NET for school so that't not the problem but, how can I now link a Command and say what it has to do in my view model, like showing a 'MessageBox' with the content of the 'TextBox' – Krowi Mar 09 '14 at 19:47
  • 1
    Usage is pretty much clear in the link you provided. Still let me explain in brief - Create a RelayCommand instance in ViewModel and in execute method you can write `MessageBox.Show(parameter)` where parameter you will pass from GUI using `CommandParameter`. Search over net you will find millions of samples for that. – Rohit Vats Mar 09 '14 at 19:54
  • 1
    I tried something out myself and posted what I got. I got it working but still have 2 more questions who I like to be answered here instead of making a new question. – Krowi Mar 09 '14 at 22:58
  • 3
    I just watched this video on youtube, https://www.youtube.com/watch?v=ysWK4e2Mtco, at `19:30` he suggested don't use CommandManager.RequerySuggested – Timeless Aug 05 '15 at 04:50
  • 2
    Slight niggle: Microsoft recommends avoiding `Predicate` in favor of `Func`. – NathanAldenSr Jun 16 '16 at 02:08
11

The benefit of using relay command is that you can bind commands directly to the ViewModels. By using commands in such a way you avoid writing code in the views codebehind.

When using relay commands, you will have to provide two methods. The first one provides a value whether the command can be executed (e.g. "CanExecuteSave"), while the other one will be responsible for executing the command ("ExecuteSave").

Dennis Kassel
  • 2,726
  • 4
  • 19
  • 30
  • Would it be possible to provide me with a easy example wit a button and a text box? Button can only activate when the text box has text. In the link I posted in the start it's a little bit confusing how to do this. Edit: your answer so far already solved 1 question :) – Krowi Mar 09 '14 at 19:11
  • Imagine some ViewModel with a property "Text", which is bound to a button's text-property. Because the relay command logic is entirely in the ViewModel, you can directly access the text of the button and validate it's value. – Dennis Kassel Mar 09 '14 at 20:30
  • I tried something out myself and posted what I got. I got it working but still have 2 more questions who I like to be answered here instead of making a new question. – Krowi Mar 09 '14 at 22:58
  • 1
    Wouldn't it be preferable to isolate the command's code in the Command class itself, rather than coupling it to a ViewModel? – vargonian Feb 28 '19 at 01:48