Rod
Rod

Reputation: 4451

How do I invoke RelayCommand with multiple parameters?

I need to change some functionality in a WPF app we've written. We use MVVM Light to implement MVVM. Whenever we've needed to pass some parameters to a method we've used MVVM Light's Messenger class. I've got to pass 3 parameters to a method, but I thought I'd try doing this without using the Messenger class, but instead I hoped I could do it using the RelayCommand() method. I did a search and found this post here on SO, from some years ago. But at least to me, I think this won't work as it's using just 1 type; string in this case. After making some trials and realizing that I'd done it wrong, I decided I could probably create a class with the 3 values I need in it as properties of the class, put it into Models folder and use

new RelayCommand<MyClass>()

So, first question, just to verify that I've got the right idea, I think I would do something like this:

new RelayCommand<MyClass>((mc) => MyMethod(mc.Prop_A, mc.Prop_B, mc.Prop_C)

Is that correct?

Assuming the answer to the above is yes, then how do I actually pass parameters to this when I bind to it in the XAML? This command is going to be associated with a button on the window/page, so I'll be using the Button's Command property. How do I actually pass in the values for the MyClass instances Prop_A, Prop_B and Prop_C?

Upvotes: 3

Views: 5549

Answers (4)

Michael Korin
Michael Korin

Reputation: 56

Here is how I did it:

  1. Your CommandRelay object takes object as parameter, you convert that object to object[] then each element to its own object.

    private RelayCommand<object> _YourCommand;
    
    public RelayCommand<object> YourCommand
    {
        get
        {
            return _YourCommand ?? (_YourCommand = new RelayCommand<object>(p =>
            {
                var    values = (object[]) p;
                int    item1  = int.Parse(values[0].ToString());
                string item2  = values[1].ToString();
                double item3  = double.Parse(values[2].ToString());
            }));
        }
    }
    
  2. Then, in xaml (Of course, your Paths in Binding must be valid references to your binded objects)

<Button Command="{Binding YourCommand}">
    	<Button.CommandParameter>
    	    <MultiBinding>
    	        <Binding Path="Item1"/>
    	        <Binding Path="Item2"/>
    	        <Binding Path="Item3"/>
    	    </MultiBinding>
    	</Button.CommandParameter>
</Button>

Upvotes: 2

XAMlMAX
XAMlMAX

Reputation: 2363

There is another approach to do this by using IMultiValueConverter:

class MultiValueConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        foreach (var item in values)
        {
            //process the properties passed in and you will need to unbox those parameters
        }
        return new object();
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}  

And then in xaml (Button code):

<Button Content="Test" Style="{StaticResource contextMenuAware}" Command="{Binding MultiParameterCommand}">
    <Button.CommandParameter>
        <MultiBinding Converter="{StaticResource MultiValueConverter}">
            <Binding Path="Items"/>
            <!-- Pass more Bindings here or static values -->
        </MultiBinding>
    </Button.CommandParameter>
</Button>  

Here is the code for inclusion of the converter:

xmlns:converter="clr-namespace:SO_app.Converters"  

And then in you Window Resources tag:

<converter:MultiValueConverter x:Key="MultiValueConverter"/>  



This way you can use Binding when passing parameters without implementing DependencyProperties.

Upvotes: 1

jegtugado
jegtugado

Reputation: 5141

So, first question, just to verify that I've got the right idea, I think I would do something like this:

new RelayCommand<MyClass>((mc) => MyMethod(mc.Prop_A, mc.Prop_B, mc.Prop_C)

This is correct.

Assuming the answer to the above is yes, then how do I actually pass parameters to this when I bind to it in the XAML? This command is going to be associated with a button on the window/page, so I'll be using the Button's Command property. How do I actually pass in the values for the MyClass instances Prop_A, Prop_B and Prop_C?

This will actually depend on where would Prop_A, Prop_B and Prop_C come from. If these properties are already inside your view model, then there is no need for you to pass parameters using XAML.

new RelayCommand<MyClass>((mc) => MyMethod(mc.Prop_A, mc.Prop_B, mc.Prop_C)

will change to

new RelayCommand<object>((param) => 
{
    // param is not used.
    var mc = this.MC; // assuming your view model holds the mc value
    MyMethod(mc.Prop_A, mc.Prop_B, mc.Prop_C);
});

We must make sure that when we load our view model, we have everything we need. Else, use an IoC to fetch whatever it is you need to.

Binding a parameter to your command is often useful for something like a calculator app where you want to pass the button value to your command such as 0 - 9.

<Button Grid.Row="0" Grid.Column="1" Content="7" Command="{Binding PerformAction}" CommandParameter="7"/>

I would want to stay away from defining classes in your view. For the separation of concern, the view should only know of the properties to be bounded to and not the models.

Upvotes: 4

Steven
Steven

Reputation: 2477

So, first question, just to verify that I've got the right idea, I think I would do something like this:

new RelayCommand<MyClass>((mc) => MyMethod(mc.Prop_A, mc.Prop_B, mc.Prop_C)

Is that correct?

Yes, it is.

How do I actually pass in the values for the MyClass instances Prop_A, Prop_B and Prop_C?

Simply create an instance of the class that holds the parameters inside your xaml as command parameter:

<Button Command="{Binding Command}">
    <Button.CommandParameter>
        <local:MyClass Prop_A="a value" Prop_B="b value" Prop_C="c value" />
    </Button.CommandParameter>
</Button>

Upvotes: 1

Related Questions