28

I would like to pass a parameter defined in the XAML (View) of my application to the ViewModel class by using the RelayCommand. I followed Josh Smith's excellent article on MVVM and have implemented the following.

XAML Code

        <Button 
        Command="{Binding Path=ACommandWithAParameter}"
        CommandParameter="Orange"
        HorizontalAlignment="Left" 
        Style="{DynamicResource SimpleButton}" 
        VerticalAlignment="Top" 
        Content="Button"/>

ViewModel Code

  public RelayCommand _aCommandWithAParameter;
  /// <summary>
  /// Returns a command with a parameter
  /// </summary>
  public RelayCommand ACommandWithAParameter
  {
     get
     {
        if (_aCommandWithAParameter == null)
        {
           _aCommandWithAParameter = new RelayCommand(
               param => this.CommandWithAParameter("Apple")
               );
        }

        return _aCommandWithAParameter;
     }
  }

  public void CommandWithAParameter(String aParameter)
  {
     String theParameter = aParameter;
  }
  #endregion

I set a breakpoint in the CommandWithAParameter method and observed that aParameter was set to "Apple", and not "Orange". This seems obvious as the method CommandWithAParameter is being called with the literal String "Apple".

However, looking up the execution stack, I can see that "Orange", the CommandParameter I set in the XAML is the parameter value for RelayCommand implemenation of the ICommand Execute interface method.

That is the value of parameter in the method below of the execution stack is "Orange",

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

What I am trying to figure out is how to create the RelayCommand ACommandWithAParameter property such that it can call the CommandWithAParameter method with the CommandParameter "Orange" defined in the XAML.

Is there a way to do this?

Why do I want to do this? Part of "On The Fly Localization" In my particular implementation I want to create a SetLanguage RelayCommand that can be bound to multiple buttons. I would like to pass the two character language identifier ("en", "es", "ja", etc) as the CommandParameter and have that be defined for each "set language" button defined in the XAML. I want to avoid having to create a SetLanguageToXXX command for each language supporting and hard coding the two character language identifier into each RelayCommand in the ViewModel.

eesh
  • 1,384
  • 2
  • 13
  • 25
  • 5
    None of the provided answers to this question helped me to create a RelayCommand object to communicate with a method requiring parameter input. If anyone is having a similar issue, this thread helped me: http://stackoverflow.com/questions/5298910/mvvm-light-relaycommand-parameters/5299030#5299030 – Jesslyn May 02 '12 at 18:28

6 Answers6

40

I don't understand why you have the extra complexity of specifying the lambda in the first place. Why not just do this:

if (_aCommandWithAParameter == null)
{           
    _aCommandWithAParameter = new RelayCommand<object>(CommandWithAParameter);
}

private void CommandWithAParameter(object state)
{
    var str = state as string;
}
Lunyx
  • 3,164
  • 6
  • 30
  • 46
Kent Boogaart
  • 175,602
  • 35
  • 392
  • 393
  • readonly Action _execute; readonly Predicate _canExecute; public RelayCommand(Action execute) : this(execute, null) { } public RelayCommand(Action execute, Predicate canExecute) { if (execute == null) throw new ArgumentNullException("execute"); _execute = execute; _canExecute = canExecute; } Action is a delegate and requires a lambda expression when calling the constructor. Not sure why, but I get a compiler error otherwise. – eesh May 01 '09 at 12:16
  • Because it's Action, not Action. Updated my post to clarify. – Kent Boogaart May 01 '09 at 12:49
  • Sorry about spacing in first comment. In any case the point is that the lambda expression is required because of the parameter argument in the RelayCommand constructor. Namely public RelayCommand(Action execute) : this(execute, null). Since the constructor of RelayCommand takes a delegate, then when creating a new RelayCommand the lambda expression as shown in my post must be used. The parameter type in the method that is used on the right hand side of the lambda expression does alter the fact that a lambda expression must be used when creating a RelayCommand. – eesh May 01 '09 at 14:34
  • eesh, I don't follow. My code in my post compiles fine. There is no need for the lambda expression. – Kent Boogaart May 01 '09 at 14:45
  • You are correct Kent. Thank you for your answer. I marked your solution as the answer because it is simpler than Paul's solution which is also correct. I think I made the mistake of misremembering trying something similar to what you suggested but I included the parens after CommandWithAParameter in the argument to the constructor. I had the same thought as you "Why complicate this with lambda expression" but assumed the original author put it there because it was required. After your comment again I tried your solution verbatim and it worked of course. – eesh May 01 '09 at 16:05
  • 1
    Don't you have to use a lambda expression if there is no parameter? It won't compile for me otherwise. – JP Richardson Jan 17 '10 at 19:38
  • @JPRichardson did you ever find the answer to this comment for yourself? I'm curious as well. – Rachael Mar 14 '13 at 17:47
  • 2
    The `RelayCommand` constructor takes `Action`, so the method you pass to it must match that delegate. The one in my above code sample does, because it takes an object parameter. If your method does not take an object parameter, the easiest way to transform is indeed to use a lambda: `new RelayCommand(_ => MyMethod())` (an underscore is the conventional name for an unused lambda parameter) – Kent Boogaart Mar 14 '13 at 19:14
14

You'll pass the param in the lambda to the command like so:

if (_aCommandWithAParameter == null)
{           
    _aCommandWithAParameter = new RelayCommand(               
        param => this.CommandWithAParameter(param)
        );        
}
Paul Alexander
  • 31,970
  • 14
  • 96
  • 151
  • Paul, I get the Compiler Error "Invalid expression ')'" when I use the syntax you suggest above. – eesh May 01 '09 at 12:24
  • the ** characters are to show emphasis - they should not be in the code you actually cut and paste. – Paul Alexander May 01 '09 at 14:41
  • 1
    Paul thanks you are right. I should have realized the ** on either side were for emphasis. Your solution works. I marked Kent's solution as the answer because it is simpler (or should I say easier to understand without the lambda expressions). – eesh May 01 '09 at 15:57
  • Would you mind editing your answer to show = new RelayCommand(param => this.CommandWithAParameter(param)). I think that will be easier for others to understand and they won't make the same cut and paste mistake I made. I am guessing I don't have enough reputation points to edit other's answers yet otherwise I would do it myself. – eesh May 01 '09 at 16:00
7

Nothing posted here before worked for me.

Turns out, all answers are missing the <object> after RelayCommand!

This works for me:

public RelayCommand<object> OKCommand
{
    get
    {
        if (_okCommand == null)
            _okCommand = new RelayCommand<object>(OkCommand_Execute);
        return _okCommand;
    }
}
private RelayCommand<object> _okCommand = null;

private void OkCommand_Execute(object obj)
{
    Result = true;
}

If you want to use aCanExecute method, use the following code:

_okCommand = new RelayCommand<object>(OkCommand_Execute, OkCommand_CanExecute);

private bool OkCommand_CanExecute(object obj) { }
JCH2k
  • 3,361
  • 32
  • 25
4

Here is a simple solution for the commandparameter as I was looking for help on the subject. I could not find anything online that was simple enough. The following solution works well when you are using a relaycommand. I had a few hyperlinks for which I needed to get the url value that was clicked using the command parameter.

Step 1: In your relay command, create a simple property that will hold the parameter object value. You could call it parametervalue or any name that you prefer.

public object ParameterValue
{
  get;
  set;
}

Step 2: In the Execute Method of the RelayCommand class, set the value or the property created above to the parameter from the Execute method.

readonly Action<object> m_execute;       // Action to execute

public void Execute(object parameter)
 {
   this.ParameterValue = parameter;
   m_execute(parameter);
 }

Step 3: Now when you can bind the CommandParameter in xaml to any value you want to retrieve when the command is executed. example:

<TextBlock>
  <Hyperlink Command="{Binding Path=NavigateUrlCmd}"
             CommandParameter="{Binding ElementName=tbwebsite, Path=Text}">
    <TextBlock Name="tbwebsite" Text="{Binding Path=website}"/>
  </Hyperlink>
</TextBlock> 

If you have a command called chickenCommand, when executed you could access the parameter: chickenCommand.ParameterValue

I hope this helps somebody. Thank you for all your previous help.

Chad
  • 7,279
  • 2
  • 24
  • 34
2

I am just trying to sell my point, check this out whether this works...

http://mywpf-visu.blogspot.com/2009/12/relay-command-how-to-pass-parameter.html

Vishu
  • 21
  • 1
1

I cannot substitute a reference to the method name for the lamda expression withing a compile error. Apparently, and by no means surprisingly, a non-static method name reference cannot be used in place of a lambda. I hardly see it as "added complexity". Consistently passing lamdas makes sense to me.

Rick O'Shea
  • 1,410
  • 19
  • 15