How to Pass a Parameter using a RelayCommand

RogerSchlueter-7899 1,446 Reputation points
2023-03-13T06:50:39.79+00:00

In spite of reading countless web sites, I cannot get my RelayCommand to handle a parameter properly. I'm worki8ng on a login window:

<Window
    x:Class="Login"
    x:Name="Login"
    ....
    <Button
        Command="{Binding Path=Login}"
        CommandParameter="{Binding ElementName=Login}"
        Content="Login" />
    ....
</Window>

I am using the RelayCommand provided by the CommunityToolkit.Mvvm, Here is the Command implemented in the ViewModel.

Private ReadOnly Property _Login As New RelayCommand(AddressOf PerformLogin)

Public ReadOnly Property Login As RelayCommand
    Get
        Return _Login
    End Get
End Property

Private Sub PerformLogin(win As Object)
    <<Process user inputs>>
End Sub

The compile -time error is

Method 'Private Sub PerformLogin(win As Object)' does not have a signature compatible with delegate 'Delegate Sub Action()'.

I understand the error but none of the numerous fixes I've tried have worked. What is the right way to accomplish this?

Developer technologies Windows Presentation Foundation
Developer technologies VB
{count} votes

Accepted answer
  1. Peter Fleischer (former MVP) 19,341 Reputation points
    2023-03-13T10:11:57.6433333+00:00

    Hi Roger,
    in PerformLogin(win As Object) you can check parameter "win" where you get CommandParameter. Try following demo:

    <Window x:Class="WpfApp1.Window046"
            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:WpfApp046"
            mc:Ignorable="d"
            Title="Roger Schlueter_230313" Height="450" Width="800">
      <Window.DataContext>
        <local:ViewModel/>
      </Window.DataContext>
      <StackPanel>
        <Label x:Name="Login" Content="First Element for Login" Margin="10"/>
        <Label x:Name="Logout" Content="Second Element for Logout" Margin="10"/>
        <Button
            Command="{Binding Path=Login}"
            CommandParameter="{Binding ElementName=Login}"
            Content="Login" Margin="10"/>
        <Button
            Command="{Binding Path=Login}"
            CommandParameter="{Binding ElementName=Logout}"
            Content="Logout" Margin="10"/>
      </StackPanel>
    </Window>
    
    

    ViewModel:

    using System;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Input;
    
    namespace WpfApp046
    {
    	public class ViewModel
    	{
    		public ICommand Login { get => new RelayCommand(CmdExec, CanCmdExec); }
    
    		private void CmdExec(object obj)
    		{
    			var element = obj as Label;
    			switch (element?.Name)
    			{
    				case "Login":
    					MessageBox.Show("Login pressed");
    					break;
    				case "Logout":
    					MessageBox.Show("Logout pressed");
    					break;
    				default:
    					break;
    			}
    		}
    
    		private bool CanCmdExec(object obj) => true;
    
    
    	}
    #pragma warning disable CS8767 // Nullability of reference types in type of parameter doesn't match implicitly implemented member (possibly because of nullability attributes).
    #pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
    	public class RelayCommand : ICommand
    	{
    		private readonly Predicate<object> _canExecute;
    		private readonly Action<object> _action;
    		public RelayCommand(Action<object> action) { _action = action; _canExecute = null; }
    		public RelayCommand(Action<object> action, Predicate<object> canExecute) { _action = action; _canExecute = canExecute; }
    		public void Execute(object o) => _action(o);
    		public bool CanExecute(object o) => _canExecute == null ? true : _canExecute(o);
    		public event EventHandler CanExecuteChanged
    		{
    			add { CommandManager.RequerySuggested += value; }
    			remove { CommandManager.RequerySuggested -= value; }
    		}
    	}
    }
    

    Result:

    x

    0 comments No comments

1 additional answer

Sort by: Most helpful
  1. Hui Liu-MSFT 48,676 Reputation points Microsoft External Staff
    2023-03-13T08:58:59.88+00:00

    Would you mind adding a custom RelayCommand? It can pass parameters.

    <TextBlock x:Name="Logintxt" Text="nico" Height="100" Width="200"/>
            <Button    Command="{Binding Path=Login}"  CommandParameter="{Binding ElementName=Logintxt ,Path=Text }"
            Content="Login" />
    
    Public Class MainWindow
    
        Public Sub New()
            InitializeComponent()
    
            DataContext = Me
        End Sub
    
        Private ReadOnly Property _Login As New MyRelayCommand(AddressOf PerformLogin)
    
        Public ReadOnly Property Login As MyRelayCommand
            Get
                Return _Login
            End Get
        End Property
    
        Private Sub PerformLogin(ByVal obj As Object)
            MessageBox.Show(obj + "hello")
        End Sub
    End Class
    
    Public Class MyRelayCommand
    
        Implements ICommand
    
        Private ReadOnly _execute As Action(Of Object)
    
        Private ReadOnly _canExecute As Predicate(Of Object)
    
        Public Sub New(ByVal execute As Action(Of Object))
    
            Me.New(execute, Nothing)
    
        End Sub
    
    
        Public Sub New(ByVal execute As Action(Of Object), ByVal canExecute As Predicate(Of Object))
    
            If execute Is Nothing Then
    
                Throw New ArgumentNullException("execute")
    
            End If
    
            _execute = execute
    
            _canExecute = canExecute
    
        End Sub
    
        Public Function CanExecute(ByVal parameter As Object) As Boolean Implements ICommand.CanExecute
    
            Return If(_canExecute Is Nothing, True, _canExecute(parameter))
    
        End Function
    
        Public Custom Event CanExecuteChanged As EventHandler Implements ICommand.CanExecuteChanged
    
            AddHandler(ByVal value As EventHandler)
    
                AddHandler CommandManager.RequerySuggested, value
    
            End AddHandler
    
            RemoveHandler(ByVal value As EventHandler)
    
                RemoveHandler CommandManager.RequerySuggested, value
    
            End RemoveHandler
    
            RaiseEvent(ByVal sender As System.Object, ByVal e As System.EventArgs)
    
            End RaiseEvent
    
        End Event
    
        Public Sub Execute(ByVal parameter As Object) Implements ICommand.Execute
    
            _execute(parameter)
    
        End Sub
    
    End Class
    
    

    The result:

    User's image


    If the response is helpful, please click "Accept Answer" and upvote it.

    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.

    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.