-1

I want to create a simple wpf user control which has 2 commands, each with a single CommandParameter which is set by a DependencyProperty in the XAML.

However the value of the property bound to the CommandParameter isn't passed through to the CommandHandler. I'm using ReactiveUI to implement the Commands. demo

The commands are being called as expected, however the CommandParameter is always 0. So I have 2 questions:

Question 1 Even if I have a single command DependencyProperty called "Command" and a single DependencyProperty parameter called "CommandParameter", the parameter isn't passed through.

Question2 How are Command and CommandParameter properties linked together? Is it a naming convention and if so does it support multiple commands (e.g. is Command1 linked to Command1Parameter?).

I'm aware of passing multiple parameters to a single command, but this is a different scenario where I have 2 separate commands. In this case I actually want to pass the same value to both commands.

I've created a simple test app to demo the problem; The MainWindow.xaml is

<Window x:Class="WpfApp1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <StackPanel>
            <local:WidgetUserControl x:Name="Widget1" Command="{Binding WidgetCommand1}" 
                                     CommandParameter="1" Command2="{Binding WidgetCommand2}" />
            
            <local:WidgetUserControl x:Name="Widget2" Command="{Binding WidgetCommand1}" 
                                     CommandParameter="1" Command2="{Binding WidgetCommand2}"/>
        </StackPanel>
    </Grid>
</Window>

The MainViewModel is:

public class MainViewModel : ReactiveObject
{
    public ReactiveCommand<int, Unit> WidgetCommand1 { get; }
    public ReactiveCommand<int, Unit> WidgetCommand2 { get; }

    public MainViewModel()
    {
        WidgetCommand1 = ReactiveCommand.Create<int>(ExecuteWidgetCommand1);
        WidgetCommand2 = ReactiveCommand.Create<int>(ExecuteWidgetCommand2);
    }

    private void ExecuteWidgetCommand1(int arg)
    {
        MessageBox.Show($"Widget Command 1: Parameter {arg}");
    }

    private void ExecuteWidgetCommand2(int arg)
    {
        MessageBox.Show($"Widget Command 2: Parameter {arg}");
    }
}

The WidgetUserControl XAML is:

<UserControl x:Class="WpfApp1.WidgetUserControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:WpfApp1"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid>
    <Border BorderBrush="Aqua" BorderThickness="2">
        <StackPanel Orientation="Horizontal" Margin="5">
            <Button Content="Button1"  
                Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl, AncestorLevel=1}, Path=Command}"/>

            <Button Content="Button2" 
                Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl, AncestorLevel=1}, Path=Command2}"/>
        </StackPanel>
    </Border>
    </Grid>
</UserControl>

The WidgetUserControl code behind looks like this:

 public partial class WidgetUserControl : UserControl
    {
        public WidgetUserControl()
        {
            InitializeComponent();
        }

        public static readonly DependencyProperty CommandProperty = DependencyProperty.Register("Command", typeof(ICommand), typeof(WidgetUserControl));
        public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.Register("CommandParameter", typeof(object), typeof(WidgetUserControl), 
            new PropertyMetadata(OnCommandParameterCallback));

        public static readonly DependencyProperty Command2Property = DependencyProperty.Register("Command2", typeof(ICommand), typeof(WidgetUserControl));
        public static readonly DependencyProperty Command2ParameterProperty = DependencyProperty.Register("Command2Parameter", typeof(object), typeof(WidgetUserControl),
            new PropertyMetadata(OnCommandParameterCallback));

        public ICommand Command
        {
            get => (ICommand)GetValue(CommandProperty);
            set => SetValue(CommandProperty, value);
        }

        public object CommandParameter
        {
            get => GetValue(CommandParameterProperty);
            set => SetValue(CommandParameterProperty, value);
        }

        public ICommand Command2
        {
            get => (ICommand)GetValue(Command2Property);
            set => SetValue(Command2Property, value);
        }

        public object Command2Parameter
        {
            get => GetValue(Command2ParameterProperty);
            set => SetValue(Command2ParameterProperty, value);
        }

        private static void OnCommandParameterCallback(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            // Not sure if this is needed
            var control = sender as WidgetUserControl;        
           control.CommandParameter = e.NewValue;
        }
    }
canice
  • 363
  • 1
  • 4
  • 16
  • 1
    If you're using xaml binding rather than rxui there is an additional xaml element you can check out called `CommandParameter` – Glenn Watson Aug 26 '21 at 02:42
  • Binding the CommandParmeters on the user control as you suggested fixed the issues. If you would like add an answer I'll mark it up as the correct answer. Thanks for the help. – canice Aug 26 '21 at 08:31

2 Answers2

1

You can use the CommandParameter in the xaml binding to pass along a parameter to the command. This is available on the Button or similar controls that support Command's.

Glenn Watson
  • 2,758
  • 1
  • 20
  • 30
0

In your code, you assigned two commands to each button. When those buttons executed the command which they had assigned to, they will pass its own command parameter.Below is the source code from ButtonBase and MS.Internal.Commands.CommandHelpers that actually do the work.

protected virtual void OnClick()
    {
        RoutedEventArgs newEvent = new RoutedEventArgs(ButtonBase.ClickEvent, this);
        RaiseEvent(newEvent);

        MS.Internal.Commands.CommandHelpers.ExecuteCommandSource(this);
    }

[SecurityCritical, SecurityTreatAsSafe]
internal static void ExecuteCommandSource(ICommandSource commandSource)
{
    CriticalExecuteCommandSource(commandSource, false);
}    

[SecurityCritical]
    internal static void CriticalExecuteCommandSource(ICommandSource commandSource, bool userInitiated)
    {
        ICommand command = commandSource.Command;
        if (command != null)
        {
            object parameter = commandSource.CommandParameter;
            IInputElement target = commandSource.CommandTarget;

            RoutedCommand routed = command as RoutedCommand;
            if (routed != null)
            {
                if (target == null)
                {
                    target = commandSource as IInputElement;
                }
                if (routed.CanExecute(parameter, target))
                {
                    routed.ExecuteCore(parameter, target, userInitiated);
                }
            }
            else if (command.CanExecute(parameter))
            {
                command.Execute(parameter);
            }
        }
    }

So basically, if you want to write a UserControl to support ICommand, you must fill your own logic like when and how to execute them. Or at least, pass the CommandParameter to the control that actually execute the command.

Alex.Wei
  • 1,798
  • 6
  • 12