I'm writing a RelayCommand that receives a parameter but I'm having trouble calling the method that implements the command.
I have the following class for RelayCommand:
using System.Windows.Input;
namespace RCadastral.MVVM
{
public class RelayCommand : ICommand
{
private Action<object> execute;
private Func<object, bool> canExecute;
public event EventHandler? CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null)
{
this.execute = execute;
this.canExecute = canExecute;
}
public bool CanExecute(object? parameter)
{
return canExecute == null || canExecute(parameter);
}
public void Execute(object? parameter)
{
execute(parameter);
}
}
}
In my view I have the following:
<Window x:Class="RCadastral.View.DetalhePassoModalWindow"
x:Name="DetalhePassoMW"
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:RCadastral.View">
<Button x:Name="Back"
Content="Voltar"
Command="{Binding CloseCommand}"
CommandParameter="{Binding ElementName=DetalhePassoMW}"/>
</Window>
And in may view modal I have the following:
public RelayCommand CloseCommand => new RelayCommand(execute => Close());
private void Close(Window window)
{
// Sime logic...
window.Close();
}
How I pass the parameter to Close() method?
I'm real confused, nothing worked. Some help please.
So basically, you did everything correctly. In this piece of code
public RelayCommand CloseCommand => new RelayCommand(execute => Close());
private void Close(Window window)
{
// Sime logic...
window.Close();
}
the Window
object which you're passing as command parameter with ElementName
from XAML will be your execute
variable in that anonymous function that you're using as the command action.
So what you can do is to cast it into a proper type and then execute your logic:
public RelayCommand CloseCommand => new RelayCommand(execute => Close(execute as Window));
But you can also take my implementation of ICommand
interface which allows you to have commands with generic parameters and more convenient way to write your code.
Here it is:
using System.Windows.Input;
namespace Wpf.Mvvm.Actions;
/// <summary>
/// Common <see cref="ICommand"/> implementation.
/// </summary>
/// <remarks>
/// Initializes a new instance of the <see cref="Command"/> class.
/// </remarks>
/// <param name="action">
/// Command action.
/// </param>
/// <param name="predicate">
/// Command predicate.
/// </param>
public sealed class Command(Action<object?> action, Func<object?, bool>? predicate) : ICommand
{
#region Fields
private readonly Func<object?, bool> _predicate = predicate ?? SuppressPredicate;
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="Command"/> class.
/// </summary>
/// <param name="action">
/// Command action.
/// </param>
/// <param name="predicate">
/// Command predicate.
/// </param>
public Command(Action<object?> action, Func<bool>? predicate)
: this(action, predicate is null ? null : p => predicate()) { }
/// <summary>
/// Initializes a new instance of the <see cref="Command"/> class.
/// </summary>
/// <param name="action">
/// Command action.
/// </param>
/// <param name="predicate">
/// Command predicate.
/// </param>
public Command(Action action, Func<object?, bool>? predicate)
: this(a => action(), predicate) { }
/// <summary>
/// Initializes a new instance of the <see cref="Command"/> class.
/// </summary>
/// <param name="action">
/// Command action.
/// </param>
/// <param name="predicate">
/// Command predicate.
/// </param>
public Command(Action action, Func<bool>? predicate)
: this(a => action(), predicate is null ? null : p => predicate()) { }
/// <summary>
/// Initializes a new instance of the <see cref="Command"/> class.
/// </summary>
/// <param name="action">
/// Command action.
/// </param>
public Command(Action<object?> action)
: this(action, null as Func<object?, bool>) { }
/// <summary>
/// Initializes a new instance of the <see cref="Command"/> class.
/// </summary>
/// <param name="action">
/// Command action.
/// </param>
public Command(Action action)
: this(a => action(), null as Func<object?, bool>) { }
#endregion
#region Public methods
/// <inheritdoc/>
public bool CanExecute(object? parameter)
=> _predicate(parameter);
/// <inheritdoc/>
public void Execute(object? parameter)
=> action(parameter);
#endregion
#region Private methods
/// <summary>
/// Predicate suppression.
/// <para>
/// Used when constructor input predicate is <see langword="null"/>.
/// </para>
/// </summary>
/// <param name="parameter">
/// Predicate parameter.
/// </param>
/// <returns>
/// Always returns <see langword="true"/>, meaning if
/// <see langword="null"/> was passed into the command constructor
/// - it will always be eligible for execution.
/// </returns>
private static bool SuppressPredicate(object? parameter)
=> true;
#endregion
#region Events
/// <inheritdoc/>
public event EventHandler? CanExecuteChanged
{
add => CommandManager.RequerySuggested += value;
remove => CommandManager.RequerySuggested -= value;
}
#endregion
}
/// <summary>
/// Parametrized <see cref="ICommand"/> implementation.
/// </summary>
public sealed class Command<T>(Action<T?> action, Func<T?, bool>? predicate) : ICommand
{
#region Fields
private readonly Func<T?, bool> _predicate = predicate ?? SuppressPredicate;
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="Command"/> class.
/// </summary>
/// <param name="action">
/// Command action.
/// </param>
/// <param name="predicate">
/// Command predicate.
/// </param>
public Command(Action<T?> action, Func<bool>? predicate)
: this(action, predicate is null ? null : p => predicate()) { }
/// <summary>
/// Initializes a new instance of the <see cref="Command"/> class.
/// </summary>
/// <param name="action">
/// Command action.
/// </param>
public Command(Action<T?> action)
: this(action, null as Func<T?, bool>) { }
#endregion
#region Public methods
/// <inheritdoc/>
public bool CanExecute(object? parameter)
=> _predicate((T?)parameter);
/// <inheritdoc/>
public void Execute(object? parameter)
=> action((T?)parameter);
#endregion
#region Private methods
/// <summary>
/// Predicate suppression.
/// <para>
/// Used when constructor input predicate is <see langword="null"/>.
/// </para>
/// </summary>
/// <param name="parameter">
/// Predicate parameter.
/// </param>
/// <returns>
/// Always returns <see langword="true"/>, meaning if
/// <see langword="null"/> was passed into the command constructor
/// - it will always be eligible for execution.
/// </returns>
private static bool SuppressPredicate(T? parameter)
=> true;
#endregion
#region Events
/// <inheritdoc/>
public event EventHandler? CanExecuteChanged
{
add => CommandManager.RequerySuggested += value;
remove => CommandManager.RequerySuggested -= value;
}
#endregion
}
So in the end your code will look like the following:
public Command<Window> CloseCommand => new(Close);
private void Close(Window window)
{
// Some logic...
window.Close();
}
Note: my code utilizes one of the recent C# features - primary constructors, if you're using older language version you can just revert that usage manually.
Hope this helps!