Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Relaycommand and parameterless execute method

I'm currently learning WPF and MVVM, I think I get most of it and how it works but I've come across something on using the RelayCommand (or DelegateCommand) that I don't understand. I think it's to do with the way delegates work.

Please note that the code below is all just in test solutions at the moment, so no live code. Also I am considering this for commands that do not require a parameter such as close and to understand why it works.

So if I take the RelayCommand that Josh Smith created( http://msdn.microsoft.com/en-us/magazine/dd419663.aspx#id0090030) I can setup a command like this:

RelayCommand updateTextContentCommand;

public ICommand UpdateTextContentCommand
{
    get
    {
        if (updateTextContentCommand == null)
        {
            updateTextContentCommand = new RelayCommand(
                param => this.UpdateTextContentCommand_Execute());
        }
        return updateTextContentCommand;
    }
}

with this execute method:

public void UpdateTextContentCommand_Execute()
{
    this.TextContent = DateTime.Now.ToString();
}

I used a simple binding to a TextBlock to see the result and the command is bound to a button. This works fine. What I don't get is the use of the lambda expression to create the command. The Action<object> expects a parameter doesn't it? so why does this code work?

If I change the code above to

if (updateTextContentCommand == null)
{
    updateTextContentCommand = new RelayCommand(
        this.UpdateTextContentCommand_Execute());
}

I get these error:

*The best overloaded method match for 'MVVM.RelayCommandTesting.Framework.RelayCommand.RelayCommand(System.Action)' has some invalid arguments

Argument 1: cannot convert from 'void' to 'System.Action'*

and removing the () after Execute gives this error:

Argument 1: cannot convert from 'method group' to 'System.Action'

But if I change the code like this:

if (updateTextContentCommand == null)
{
    updateTextContentCommand = new RelayCommand(
        this.UpdateTextContentCommand_Execute);
}

public void UpdateTextContentCommand_Execute(object param)
{
    this.TextContent = DateTime.Now.ToString();
}

it complies and runs fine. If I change the view to use CommandParameter I can then use param to set the text content using this method but if I use the lambda style I have to pass a parameter to the line so its like this param => this.UpdateTextContentCommand_Execute(param).

In my test I'm hard coding the CommandParameter value but I guess it would most likely be data bound to a property of the ViewModel in a real system so you would be able to pass the parameter in the lambda style.

Can anyone explain why the parameterless version works with the lambda style please?

Thanks for taking the time to read this.

It seems the question below had some questions about the lambda as well but I don't see that it answers my question.

Passing a parameter using RelayCommand defined in the ViewModel (from Josh Smith example)

like image 801
Kioshiki Avatar asked Aug 05 '12 12:08

Kioshiki


1 Answers

The constructor parameter is a delegate that has the following signature:

void MethodName(T parameter)

where parameter is of type T (in the case of the RelayCommand this will be of type system.Object.

This code:

param => this.UpdateTextContentCommand_Execute()

is a lambda expression that essentially expands to this:

void AnonymousMethod(object param)
{
    this.UpdateTextContentCommand_Execute();
}

So in this case you are passing in a parameter (param) you are just not using it. If you understand this then you should now realise why your other examples behave the way they do.

Example 1

if (updateTextContentCommand == null)
{
    updateTextContentCommand = new RelayCommand(
        this.UpdateTextContentCommand_Execute());
}

Here you are calling the method which returns void. The constructor is expecting something that matches the Action<T> delegate hence the error.

Example 2

If you then remove the brackets like this:

if (updateTextContentCommand == null)
{
    updateTextContentCommand = new RelayCommand(
        this.UpdateTextContentCommand_Execute);
}

Think of this as being a bit like subscribing to an event:

myObject.myevent += new Action<object>(this.UpdateTextContentCommand_Execute);

which can be shortened to:

myObject.myevent += this.UpdateTextContentCommand_Execute;

So the constructor accepts any method that has a signature that matches the Action<T> delegates signature i.e.

void UpdateTextContentCommand_Execute(object parameter)

Your method has the following signature:

void UpdateTextContentCommand_Execute()

As you can see the signatures don't match so the compiler complains.

When you update your UpdateTextContentCommand_Execute method to accept an object parameter it's signature now matches which is why it now works.

like image 65
Benjamin Gale Avatar answered Sep 26 '22 16:09

Benjamin Gale