How to Use the Relaycommand in Wpf

How can I use the RelayCommand in wpf?

Relay command doesn't exist in WPF, it is just a external class that raised to prominence after it was defined in this MSDN article. You need to write it yourself if you want to use it.

Otherwise you can you the Delegate command from the WPF toolkit here which has a little bit of extra functionality over the RelayCommand code.


Ah, the question changed while I was typing this answer. Assuming that you are using the RelayCommand as defined above you need to supply it with one or two delegates, one that returns a bool which determines whether the command is in a valid state to be run, and a second which returns nothing and actually runs the command. If you don't supply a "CanRun" delegate then the command will consider that it is always in a valid state. The code used in the article:

RelayCommand _saveCommand;
public ICommand SaveCommand
{
get
{
if (_saveCommand == null)
{
_saveCommand = new RelayCommand(param => this.Save(),
param => this.CanSave );
}
return _saveCommand;
}
}

Declares a RelayCommand that will call the Save() method when triggered and return the CanSave property as a test for validity. When this command is bound to a button in WPF the IsEnabled property of the Button will match the CanSave property of the ViewModel and when the button is clicked (assuming it is enabled) the Save() method will be called on the ViewModel.

How to use the RelayCommand Class

I don't think the fix you used is correct. The VB version of the constructor should be:

Public Sub New()
DisplayEnter = New RelayCommand(Sub(o) DisplayEnterCommand(CType(o, Numbers)))
End Sub

You have to be careful with coding: the first change that compiles is not necessarily the correct fix.

Full implementation of Relay Command - can it be applied to all cases?

1) ICommand only has methods that include a parameter. If you don't specify a parameter in XAML, null is used.

https://msdn.microsoft.com/en-us/library/system.windows.input.icommand(v=vs.110).aspx

2) Yes, you can effect CanExecute without a CommandParameter. See below, it uses the viewmodel's string property "MyData" in CanExecute.

MainWindow.xaml

<Window x:Class="WpfApplication8.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:WpfApplication8"
mc:Ignorable="d"
FocusManager.FocusedElement="{Binding ElementName=tb}"
SizeToContent="Height"
Title="MainWindow" Width="525">
<Window.DataContext>
<local:MainWindowViewModel />
</Window.DataContext>
<StackPanel>
<Label Content="MyData (CanExecute returns false if this is whitespace)" />
<TextBox Name="tb" Text="{Binding MyData, UpdateSourceTrigger=PropertyChanged}" Margin="5" />
<Button Content="Without XAML CommandParameter" Margin="5" Command="{Binding Command1}" />
<Button Content="With XAML CommandParameter" Margin="5" Command="{Binding Command1}" CommandParameter="{Binding MyData}" />
</StackPanel>
</Window>

MainWindow.xaml.cs

using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Input;

namespace WpfApplication8
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}

public class MainWindowViewModel : INotifyPropertyChanged
{
private ICommand command1;
public ICommand Command1 { get { return command1; } set { command1 = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Command1))); } }

private string myData;
public string MyData { get { return myData; } set { myData = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(MyData))); } }

public event PropertyChangedEventHandler PropertyChanged;

public MainWindowViewModel()
{
Command1 = new RelayCommand<object>(Command1Execute, Command1CanExecute);
}

private bool Command1CanExecute(object obj)
{
// Only allow execute if MyData has data
return !string.IsNullOrWhiteSpace(MyData);
}

private void Command1Execute(object obj)
{
MessageBox.Show($"CommandParameter = '{obj}'");
}
}

public class RelayCommand<T> : ICommand
{
#region Fields

readonly Action<T> _execute = null;
readonly Predicate<T> _canExecute = null;

#endregion

#region Constructors

/// <summary>
/// Initializes a new instance of <see cref="DelegateCommand{T}"/>.
/// </summary>
/// <param name="execute">Delegate to execute when Execute is called on the command. This can be null to just hook up a CanExecute delegate.</param>
/// <remarks><seealso cref="CanExecute"/> will always return true.</remarks>
public RelayCommand(Action<T> execute)
: this(execute, null)
{
}

/// <summary>
/// Creates a new command.
/// </summary>
/// <param name="execute">The execution logic.</param>
/// <param name="canExecute">The execution status logic.</param>
public RelayCommand(Action<T> execute, Predicate<T> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");

_execute = execute;
_canExecute = canExecute;
}

#endregion

#region ICommand Members

///<summary>
///Defines the method that determines whether the command can execute in its current state.
///</summary>
///<param name="parameter">Data used by the command. If the command does not require data to be passed, this object can be set to null.</param>
///<returns>
///true if this command can be executed; otherwise, false.
///</returns>
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute((T)parameter);
}

///<summary>
///Occurs when changes occur that affect whether or not the command should execute.
///</summary>
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}

///<summary>
///Defines the method to be called when the command is invoked.
///</summary>
///<param name="parameter">Data used by the command. If the command does not require data to be passed, this object can be set to <see langword="null" />.</param>
public void Execute(object parameter)
{
_execute((T)parameter);
}

#endregion
}
}

Why RelayCommand

Commands are used to separate the semantics and the object that invokes a command from the logic that executes the command i.e. it separates UI component from the logic that needs to be executed on command invocation. So, that you can test business logic separately using test cases and also your UI code is loosely coupled to business logic.

Now, that being said let's pick your questions one by one:

What job does it do?

I have added the details above. Hope it clears the usage of commands.



Is it usable for all commands in my form?

Some controls exposed Command DependencyProperty like Button, MenuItem etc. which have some default event registered to it. For Button it's Click event. So, if you bind ICommand declared in ViewModel with Button's Command DP, it will be invoked whenever button is clicked on.

For other events, you can bind using Interactivity triggers. Refer to the sample here how to use them to bind to ICommand in ViewModel.



How do I make the button disable when (a) certain text box(es) are not
filled in?

The link you posted doesn't provide complete implementation of RelayCommand. It lacks the overloaded constructor to set CanExecute predicate which plays a key role in enabling/disabling the UI control to which your command is bound to.

Bound TextBoxes with some properties in ViewModel and in CanExecute delegate returns false if any of the bound properties are null or empty which automatically disabled the control to which command is bound to.


Full implementation of RelayCommand:

public class RelayCommand<T> : ICommand
{
#region Fields

readonly Action<T> _execute = null;
readonly Predicate<T> _canExecute = null;

#endregion

#region Constructors

/// <summary>
/// Initializes a new instance of <see cref="DelegateCommand{T}"/>.
/// </summary>
/// <param name="execute">Delegate to execute when Execute is called on the command. This can be null to just hook up a CanExecute delegate.</param>
/// <remarks><seealso cref="CanExecute"/> will always return true.</remarks>
public RelayCommand(Action<T> execute)
: this(execute, null)
{
}

/// <summary>
/// Creates a new command.
/// </summary>
/// <param name="execute">The execution logic.</param>
/// <param name="canExecute">The execution status logic.</param>
public RelayCommand(Action<T> execute, Predicate<T> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");

_execute = execute;
_canExecute = canExecute;
}

#endregion

#region ICommand Members

///<summary>
///Defines the method that determines whether the command can execute in its current state.
///</summary>
///<param name="parameter">Data used by the command. If the command does not require data to be passed, this object can be set to null.</param>
///<returns>
///true if this command can be executed; otherwise, false.
///</returns>
public bool CanExecute(object parameter)
{
return _canExecute == null || _canExecute((T)parameter);
}

///<summary>
///Occurs when changes occur that affect whether or not the command should execute.
///</summary>
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}

///<summary>
///Defines the method to be called when the command is invoked.
///</summary>
///<param name="parameter">Data used by the command. If the command does not require data to be passed, this object can be set to <see langword="null" />.</param>
public void Execute(object parameter)
{
_execute((T)parameter);
}

#endregion
}

How to trigger a RelayCommand manually in WPF?

DataGrid.PreviewKeyDown event is called earlier that Window.KeyPress.

Either use PreviewKeyDown event in MainWindow, or specify all the actions in the grid:

<DataGrid>
<i:Interaction.Triggers>
<i:EventTrigger EventName="PreviewKeyDown">
<mvvm:CallMethodAction Method="DoSomething" />
<mvvm:EventToCommand Command="{Binding KeyDownLocationDG}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</DataGrid>`

In the second case you should also set KeyEventArgs.IsHandled = true; in order to prevent bubling the event down to Window, but it may have undesired effects

Bind a WPF RelayCommand from DataTemplate to a Button inside a UserControl

Well I must really thank @ΩmegaMan and other requesting some code to work.

I build a small Toy Project which really helped debug the issue.

What did I change? Actually it was quite simple, inside the DataTemplate, I just needed to use a relative source pointing to the Window and use a DataContext reference to the command property of the Window ViewModel (which is referenced in the Window Datacontext).

I ended up changing:

<Window.Resources>
<DataTemplate DataType="{x:Type vm:vmLearnSpeak}">
<local:viewLearnSpeak Command="{Binding cmdVMNotKnown, ElementName=root}" />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:vmLearnWrite}">
<local:viewLearnWrite />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:vmLearnListen}">
<local:viewLearnListen />
</DataTemplate>
</Window.Resources>

To:

<Window.Resources>
<DataTemplate DataType="{x:Type vm:vmLearnSpeak}">
<local:viewLearnSpeak Command="{Binding DataContext.cmdVMNotKnown, RelativeSource={RelativeSource AncestorType=local:wndSession}}" />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:vmLearnWrite}">
<local:viewLearnWrite />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:vmLearnListen}">
<local:viewLearnListen />
</DataTemplate>
</Window.Resources>

WPF MVVM RelayCommand Action, canExecute, parameter

The following view model implementation should work:

public class ViewModel
{
public ViewModel()
{
ModificarLicenciaCommand = new RelayCommand<SfDataGrid>(ModificarLicencia, CanModificarLicencia);
}

private ICommand _modificarLicenciaCommand;
public ICommand ModificarLicenciaCommand
{
get { return _modificarLicenciaCommand; }
set { _modificarLicenciaCommand = value; }
}

private void ModificarLicencia(SfDataGrid dataGrid)
{
// Modificar licencia
}

private bool CanModificarLicencia(SfDataGrid dataGrid)
{
return true;
}
}


Related Topics



Leave a reply



Submit