How to use commands within SimpleMVVM toolkit

Aug 13, 2011 at 4:41 PM

Tony,

After continuing to work through my prior challenge of how go about finding the root view model I think I have a partial solution where I use a helper class to find the ancestor object. In reading your comments as well as what is in the documentation I think I am in a scenario where instead of relying on the VM to carry the parameter to the method I need to pass the parameter from the view to the VM using command and command parameters. In reading through the SImpleMVVM docuemtnation I see where there is a DelegateCommand class baked into the toolkit.  While I am trying to call the command I am receiving an error that no definition can be found on the CanExecute,  here is my snippet

 

public ICommand GetRecordDetailCommand
        {
            get
            {
                return new DelegateCommand<string>(GetRecordDetail, s => this.CanGetRecordDetail);
            }
        }


Method
 private void GetRecordDetail(string RecordID)
        {
            MessageBox.Show(RecordID.ToString());
        }
Can you provide some guidance on what I am missing. I'd like to try to use the delegate command in the toolkit vs using a separate Delegate command or Relay command class if possible just to minimize the code usage.

If you can point me to a sample or some other documentation on how commands work within SimpleMVVM I would appreciate it.

thank you

 

Aug 13, 2011 at 10:57 PM

I have to admit I'm not a huge fan of commands, as you can see from my blog post on the subject.  Most of the time you can accomplish the same thing more easily with an EventTrigger with a CallMethodAction.  Most of the time this isn't a problem because the method on the ViewModel which you are executing can get data from other properties on the ViewModel, so there's no need for a parameter on the method, which is fine because the method you are calling via the CallMethodAction cannot have any parameters. However, there are times when the data you want to pass into a method does not exist on the ViewModel and is "hard-coded" in the View.  That's when using commands becomes an option, because commands can invoke methods that take one parameter.  Simple Mvvm Toolkit supports this with a generic DelegateCommand.  Things get more interesting, however, when you also would like to enable of disable the button based on a condition. In this case, you want to pass a method that accepts the same parameter as your command method but returns boolean.

This relates to the problem you're facing.  Your CanGetRecordDetail method must accept a string parameter and return boolean.  Also, there's no need for a lambda -- just pass the name of the CanGetRecordDetail method as the last parameter.

Another thing to note is that the signature of your GetRecordDetailCommand property is not what it should be.  In v. 2 of the toolkit I updated the mvvmcommand code snippet so that the property returns the concrete DelegateCommand<T> rather than ICommand. This is important when you want to call the RaiseCanExecute method, which is not part of the ICommand interface, but which you should invoke when you want the CanExecute method to be called.

Here is a sample app that demonstrates how to use commands correctly with Simple Mvvm Toolkit.  The ViewModel looks like this:

#region Properties

private int amount;
public int Amount
{
    get { return amount; }
    set
    {
        amount = value;
        NotifyPropertyChanged(m => m.Amount);

        // Raise can execute changed event on command
        AddCommand.RaiseCanExecuteChanged();
    }
}

private int total;
public int Total
{
    get { return total; }
    set
    {
        total = value;
        NotifyPropertyChanged(m => m.Total);
    }
}

#endregion

#region Methods

void Add(int num)
{
    Total = Amount + num;
}

bool CanAdd(int num)
{
    return Amount > 0;
}

#endregion

#region Commands

private DelegateCommand<int> addCommand;
public DelegateCommand<int> AddCommand
{
    get { return addCommand ?? (addCommand = new DelegateCommand<int>(Add, CanAdd)); }
    private set { addCommand = value; }
}

#endregion

Notice that the DelegateCommand is initialized with two parameters: the Add method and also CanAdd, and that CanAdd accepts an int and returns bool. Also notice that RaiseCanExecuteChanged is called in the property setter for Amount. The View looks like this:

<Button Content="Add 1" Grid.Row="2" Height="30" Width="60"
        Command="{Binding AddCommand}"
        CommandParameter="1"/>
<Button Content="Add 2" Grid.Row="2" Grid.Column="3" Height="30" Width="60" 
        Command="{Binding AddCommand}"
        CommandParameter="2"/>

Notice that each button calls the same command but passes a different parameter, which is a value that does not reside as a property in the ViewModel.  This is the best scenario for using commands.

Cheers,

Tony

Aug 14, 2011 at 8:55 PM

P.S. I updated the Programming Reference topic for DelegateCommand in the online documentation.