[wpf] Passing two command parameters using a WPF binding

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.

This question is related to wpf binding path command

The answer is


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;
}

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();
    }

    ...
}

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>

This task can also be solved with a different approach. Instead of programming a converter and enlarging the code in the XAML, you can also aggregate the various parameters in the ViewModel. As a result, the ViewModel then has one more property that contains all parameters.

An example of my current application, which also let me deal with the topic. A generic RelayCommand is required: https://stackoverflow.com/a/22286816/7678085

The ViewModelBase is extended here by a command SaveAndClose. The generic type is a named tuple that represents the various parameters.

public ICommand SaveAndCloseCommand => saveAndCloseCommand ??= new RelayCommand<(IBaseModel Item, Window Window)>
    (execute =>
    {
        execute.Item.Save();
        execute.Window?.Close(); // if NULL it isn't closed.
    },
    canExecute =>
    {
        return canExecute.Item?.IsItemValide ?? false;
    });
private ICommand saveAndCloseCommand;

Then it contains a property according to the generic type:

public (IBaseModel Item, Window Window) SaveAndCloseParameter 
{ 
    get => saveAndCloseParameter ; 
    set 
    {
        SetProperty(ref saveAndCloseParameter, value);
    }
}
private (IBaseModel Item, Window Window) saveAndCloseParameter;

The XAML code of the view then looks like this: (Pay attention to the classic click event)

<Button 
    Command="{Binding SaveAndCloseCommand}" 
    CommandParameter="{Binding SaveAndCloseParameter}" 
    Click="ButtonApply_Click" 
    Content="Apply"
    Height="25" Width="100" />
<Button 
    Command="{Binding SaveAndCloseCommand}" 
    CommandParameter="{Binding SaveAndCloseParameter}" 
    Click="ButtonSave_Click" 
    Content="Save"
    Height="25" Width="100" />

and in the code behind of the view, then evaluating the click events, which then set the parameter property.

private void ButtonApply_Click(object sender, RoutedEventArgs e)
{
    computerViewModel.SaveAndCloseParameter = (computerViewModel.Computer, null);
}

private void ButtonSave_Click(object sender, RoutedEventArgs e)
{
    computerViewModel.SaveAndCloseParameter = (computerViewModel.Computer, this);
}

Personally, I think that using the click events is not a break with the MVVM pattern. The program flow control is still located in the area of ??the ViewModel.


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>

Examples related to wpf

Error: the entity type requires a primary key Reportviewer tool missing in visual studio 2017 RC Pass command parameter to method in ViewModel in WPF? Calling async method on button click Setting DataContext in XAML in WPF How to resolve this System.IO.FileNotFoundException System.Windows.Markup.XamlParseException' occurred in PresentationFramework.dll? Binding an Image in WPF MVVM How to bind DataTable to Datagrid Setting cursor at the end of any text of a textbox

Examples related to binding

Binding ComboBox SelectedItem using MVVM Binding List<T> to DataGridView in WinForm Angularjs Template Default Value if Binding Null / Undefined (With Filter) Bootstrap control with multiple "data-toggle" How does the 'binding' attribute work in JSF? When and how should it be used? WPF Binding to parent DataContext How to get evaluated attributes inside a custom directive How can I use a C++ library from node.js? bind/unbind service example (android) Datagrid binding in WPF

Examples related to path

Get Path from another app (WhatsApp) How to serve up images in Angular2? How to create multiple output paths in Webpack config Setting the correct PATH for Eclipse How to change the Jupyter start-up folder Setting up enviromental variables in Windows 10 to use java and javac How do I edit $PATH (.bash_profile) on OSX? Can't find SDK folder inside Android studio path, and SDK manager not opening Get the directory from a file path in java (android) Graphviz's executables are not found (Python 3.4)

Examples related to command

'ls' is not recognized as an internal or external command, operable program or batch file Command to run a .bat file how to run python files in windows command prompt? Run a command shell in jenkins How to recover the deleted files using "rm -R" command in linux server? Split text file into smaller multiple text file using command line ansible : how to pass multiple commands Jmeter - Run .jmx file through command line and get the summary report in a excel cocoapods - 'pod install' takes forever Daemon not running. Starting it now on port 5037