Passing two command parameters using a WPF binding

197,943

Solution 1

Firstly, if you're doing MVVM you would typically have this information available to your VM via separate properties bound from the view. That saves you having to pass any parameters at all to your commands.

However, you could also multi-bind and use a converter to create the parameters:

<Button Content="Zoom" Command="{Binding MyViewModel.ZoomCommand">
    <Button.CommandParameter>
        <MultiBinding Converter="{StaticResource YourConverter}">
             <Binding Path="Width" ElementName="MyCanvas"/>
             <Binding Path="Height" ElementName="MyCanvas"/>
        </MultiBinding>
    </Button.CommandParameter>
</Button>

In your converter:

public class YourConverter : IMultiValueConverter
{
    public object Convert(object[] values, ...)
    {
        return values.Clone();
    }

    ...
}

Then, in your command execution logic:

public void OnExecute(object parameter)
{
    var values = (object[])parameter;
    var width = (double)values[0];
    var height = (double)values[1];
}

Solution 2

In the converter of the chosen solution, you should add values.Clone() otherwise the parameters in the command end null

public class YourConverter : IMultiValueConverter
{
    public object Convert(object[] values, ...)
    {
        return values.Clone();
    }

    ...
}

Solution 3

Use Tuple in Converter, and in OnExecute, cast the parameter object back to Tuple.

public class YourConverter : IMultiValueConverter 
{      
    public object Convert(object[] values, ...)     
    {   
        Tuple<string, string> tuple = new Tuple<string, string>(
            (string)values[0], (string)values[1]);
        return (object)tuple;
    }      
} 

// ...

public void OnExecute(object parameter) 
{
    var param = (Tuple<string, string>) parameter;
}

Solution 4

If your values are static, you can use x:Array:

<Button Command="{Binding MyCommand}">10
  <Button.CommandParameter>
    <x:Array Type="system:Object">
       <system:String>Y</system:String>
       <system:Double>10</system:Double>
    </x:Array>
  </Button.CommandParameter>
</Button>

Solution 5

About using Tuple in Converter, it would be better to use 'object' instead of 'string', so that it works for all types of objects without limitation of 'string' object.

public class YourConverter : IMultiValueConverter 
{      
    public object Convert(object[] values, ...)     
    {   
        Tuple<object, object> tuple = new Tuple<object, object>(values[0], values[1]);
        return tuple;
    }      
} 

Then execution logic in Command could be like this

public void OnExecute(object parameter) 
{
    var param = (Tuple<object, object>) parameter;

    // e.g. for two TextBox object
    var txtZip = (System.Windows.Controls.TextBox)param.Item1;
    var txtCity = (System.Windows.Controls.TextBox)param.Item2;
}

and multi-bind with converter to create the parameters (with two TextBox objects)

<Button Content="Zip/City paste" Command="{Binding PasteClick}" >
    <Button.CommandParameter>
        <MultiBinding Converter="{StaticResource YourConvert}">
            <Binding ElementName="txtZip"/>
            <Binding ElementName="txtCity"/>
        </MultiBinding>
    </Button.CommandParameter>
</Button>
Share:
197,943

Related videos on Youtube

JasonD
Author by

JasonD

I am the CTO of a Denver based SaaS software startup specializing in customizable mobile online and offline field data collection, GPS tracking, scheduling, routing and asset data capture software. I was previously a systems architect for an enterprise telecommunications software company in Denver.

Updated on February 12, 2021

Comments

  • JasonD
    JasonD about 3 years

    I have a command which I am executing from my XAML file using the following standard syntax:

    <Button Content="Zoom" Command="{Binding MyViewModel.ZoomCommand}"/>
    

    This worked fine until I realized that I needed TWO pieces of information from the view in order to make this operation complete the way users expect (the width and height of the canvas specfically).

    It seems like it's possible to pass an array as an argument to my command, but I don't see there being a way to specify the binding to my two canvas properties in the CommandParameter:

    <Button Content="Zoom" 
            Command="{Binding MyViewModel.ZoomCommand" 
            CommandParameter="{Binding ElementName=MyCanvas, Path=Width}"/>
    

    How do I pass both Width and Height to my command? It doesn't seem like this is possible using commands from XAML and I need to wire up a click handler in my codebehind to get this information to pass to my zoom method.

  • JasonD
    JasonD over 14 years
    Thanks Kent - that was exactly what I was looking for. I like your first approach better so that the VM knows the "state" of the view through a binding without me having to pass parameters at all, but I can still test it. I'm not sure that's going to work for me here, as I need the view to make the canvas as big as possible and pass this value to the VM. If I bind it, won't I have to set the width in the VM? In which case, the VM is bound to the view?
  • aroon65
    aroon65 over 14 years
    @Jason: you can do it either way. That is, have the view push changes back to the view model, or have the view model push changes to the view. A TwoWay binding will result in either option being available to you.
  • Alex David
    Alex David about 10 years
    in my program OnExecute method parameter is a array with null values but, in the converter the values are as expected
  • SubmarineX
    SubmarineX about 10 years
    I find that parameter is null in OnExecute method, also YourConverter.Convert() wasn't called after click the button. Why?
  • adminSoftDK
    adminSoftDK over 9 years
    This does not work, when a button is pressed, the parameters are null
  • adminSoftDK
    adminSoftDK over 9 years
    Hi, this addition with Clone() makes it work :) Can you please explain, what difference it does. Becuase I dont understand why it needs that Clone() to work? Thank you.
  • Justin Pihony
    Justin Pihony about 9 years
    Yah, I ran into the null problem also. So I just edited the answer to add what Daniel added to it below (please upvote his if this helped)
  • Ludvig W
    Ludvig W over 5 years
    @KentBoogaart in your answer you said the VM should already have these parameters. Do you mind quickly tell me how to provide them in order to remove the command parameters? Thanks!
  • maxp
    maxp over 5 years
    I might be wrong, but this (line 1267) looks like it could be the reason to me: referencesource.microsoft.com/#PresentationFramework/src/…
  • vargonian
    vargonian about 5 years
    @KentBoogaart I have the same question as Lurr. I don't see how you could avoid command parameters without defining your command logic from within your ViewModel, which feels like a code smell (i.e. the Command class should contain its logic, right?)
  • mins
    mins almost 5 years
    "If your values are static": What is a static resource? For example the question mentions Canvas Width and Height. These values are not constant, but are they static? What would be the XAML in this case?
  • Maxence
    Maxence almost 5 years
    I should have written "constant" instead of "static". A static resource is a resource that don't change during the execution. If you use SystemColors for example, you should use DynamicResource instead of StaticResource because the user can change the system colors via Control Panel during the execution. Canvas Width and Height are not resources and are not static. There are instance properties inherited from FrameworkElement.
  • Caleb W.
    Caleb W. about 4 years
    I like this one since it is more clear how many parameters the converter supports. Good for just two parameters! (Plus you showed XAML and Command execute function for full coverage)