MVVM – ICommand, RelayCommand and EventToCommand
In this Post, I will take a closer look at one of the important components of any Model-View-ViewModel application: ICommand its implementation in RelyCommand in WPF-MVVM architecture.
Introduction
MVVM – Model – View & ViewModel
Whenever any button is pressed on the View screen, XAML has its code-behind ButtonClick event. But in MVVM architecture, there is no room for code-behind to keep the application loosely coupled, amd that is because ICommand was introduced. It is a way to trigger ViewModel when action is performed on View.Let’s start with RelayCommand which is implementation of ICommand.
RelayCommand is going to be inherited from ICommand.
- Event: EventHandler CanExecuteChanged
This event runs asynchronously in the background, It keeps track if logic in the method CanExecute() has changed. - CanExecute method
CanExecute plays a vital role. Take a scenario, let’s say you have a user registration page ( in our case it is the ViewModel.cs) and it has some TextBoxes and one Register button at the bottom. Initially, the button has to be disabled. The button will only be enabled when a user starts filling the form. Otherwise, it is useless for a button to work if
it has nothing to process. Don’t worry we will see this in action. - Execute method
The execution of an Execute method depends on what CanExecute returns.
Note that in Windows Presentation Foundation (WPF), the CanExecuteChanged event does not need to be raised manually. A class named CommandManager is observing the user interface and calls the CanExecute method when it deems it necessary.
Like we said above, if the user starts filling up a form then Register Button will be enabled and then after clicking on a button we will process the information that the user has entered.
But the Register Button remains disabled until the user starts typing. This means our Execute method is not going to be triggered unless and until we have some information to process.
Now our relay class (in our case RelayCommand) is going to need 2 delegates.
Step 1: Implement RelayCommand with following code:
using System;
using System.Windows.Input;
namespace WPF_MVVM_ICommand
{
public class RelayCommand : ICommand
{
Action<object> _execute;
Func<object, bool> _canExecute;
public RelayCommand(Action<object> execute, Func<object, bool> canExecute)
{
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
if (_canExecute != null)
{
return _canExecute(parameter);
}
else
{
return false;
}
}
public event EventHandler CanExecuteChanged
{
add
{
CommandManager.RequerySuggested += value;
}
remove
{
CommandManager.RequerySuggested -= value;
}
}
public void Execute(object parameter)
{
_execute(parameter);
}
}
}
Code description:
As we see in the above code, RelayCommand inherites from ICommand and implements the Delegate methods and Event as described before.
tep 2: Implement ViewModel class with following code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Prism.Commands;
using Prism.Mvvm;
using System.Windows.Input;
namespace WPF_MVVM_ICommand.ViewModel
{
class ViewModel : BindableBase
{
#region Properties
private string _userName;
public string UserName
{
get { return _userName; }
set { SetProperty(ref _userName, value); }
}
private string _age;
public string Age
{
get { return _age; }
set { SetProperty(ref _age, value); }
}
private string _emailId;
public string EmailId
{
get { return _emailId; }
set { SetProperty(ref _emailId, value); }
}
private bool _isButtonClicked;
public bool IsButtonClicked
{
get { return _isButtonClicked; }
set { SetProperty(ref _isButtonClicked, value); }
}
#endregion
#region ICommands
public ICommand RegisterButtonClicked { get; set; }
public ICommand ResetButtonClicked { get; set; }
#endregion
#region Constructor
public ViewModel()
{
RegisterButtonClicked = new RelayCommand(RegisterUser, CanUserRegister);
ResetButtonClicked = new RelayCommand(ResetPage, CanResetPage);
}
#endregion
#region Event Methods
private void RegisterUser(object value)
{
IsButtonClicked = true;
}
private bool CanUserRegister(object value)
{
if (string.IsNullOrEmpty(UserName))
{
return false;
}
else
{
return true;
}
}
private void ResetPage(object value)
{
IsButtonClicked = false;
UserName = Age = EmailId = "";
}
private bool CanResetPage(object value)
{
if (string.IsNullOrEmpty(UserName)
|| string.IsNullOrEmpty(EmailId)
|| string.IsNullOrEmpty(Age))
{
return false;
}
else
{
return true;
}
}
#endregion
}
}
Code Description:
First we should have installed package Prism.Core via Manage NuGet Package Manager in the VS and add the namespace Prism.mvvm. This package contains definition of base class BindableBase. This class class implements the INotifyPropertyChanged interface and provides the API to declare bindable property with minimum coding.
In this class: Properties: UserName, age and emailId is defined. Two other Properties: RegisterButtonClicked and ResetButtonclicked are defined.
public ICommand RegisterButtonClicked { get; set; }
public ICommand ResetButtonClicked { get; set; }
Constructor where commands are initialized:
public ViewModel()
{
RegisterButtonClicked = new RelayCommand(RegisterUser, CanUserRegister);
ResetButtonClicked = new RelayCommand(ResetPage, CanResetPage);
}
Step 3: Implement the UI (MainWindow.xaml) with followng code:
<Window x:Class="WPF_MVVM_ICommand.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewmodel="clr-namespace:WPF_MVVM_ICommand.ViewModel"
mc:Ignorable="d"
Title="MainWindow" Height="200" Width="320">
<Window.Resources>
<viewmodel:ViewModel x:Key="vm"></viewmodel:ViewModel>
</Window.Resources>
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Center" DataContext="{Binding Source={StaticResource vm}}">
<Grid HorizontalAlignment="Center">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="5*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Label x:Name="LabelUserName"
Content="User Name:"
Margin="0 10 0 0"/>
<Label x:Name="LabelAge"
Content="Age:"
Grid.Row="1"/>
<Label x:Name="LabelEmailId"
Content="Email:"
Grid.Row="2"/>
<TextBox x:Name="TextBoxUserName"
Text="{Binding UserName}"
Height="20"
Width="150"
Margin="0 10 0 0"
Grid.Column="1"/>
<TextBox x:Name="TextBoxAge"
Text="{Binding Age}"
Height="20"
Width="150"
Grid.Column="1"
Grid.Row="1"/>
<TextBox x:Name="TextBoxEmail"
Text="{Binding EmailId}"
Height="20"
Width="150"
Grid.Column="1"
Grid.Row="2"/>
<StackPanel x:Name="StackPanelButtons"
Orientation="Horizontal"
Grid.ColumnSpan="2"
Grid.Row="3" >
<Button x:Name="ButtonRegister"
Height="20"
Width="100"
Content="Register"
HorizontalAlignment="Center"
Margin="20 10 0 0"
Command="{Binding RegisterButtonClicked}"/>
<Button x:Name="ButtonReset"
Height="20"
Width="100"
Content="Reset"
HorizontalAlignment="Center"
Margin="20 10 0 0"
Command="{Binding ResetButtonClicked}"/>
</StackPanel>
<TextBlock x:Name="TextBlockMessage"
HorizontalAlignment="Center"
Margin="20 8 0 0"
Grid.Row="4"
Grid.ColumnSpan="2">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Text"
Value="Enter details to register!">
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding IsButtonClicked}" Value="True">
<Setter Property="Text"
Value="{Binding UserName, StringFormat='User: {0} is successfully registered!'}">
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</Grid>
</StackPanel>
</Window>
Code Description:
In the line: 6 we define the NameSpace to ViewModel which needed to call to the class:
"xmlns:viewmodel="clr-namespace:WPF_MVVM_ICommand.ViewModel"
Line 9 to 12: a Windows Resources is defined for Viewmodel with x:Key as vm and it is used for DataContext to Binding Source StaticResource vm as seen in the following code.
<Window.Resources>
<viewmodel:ViewModel x:Key="vm"></viewmodel:ViewModel>
</Window.Resources>
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Center" DataContext="{Binding Source={StaticResource vm}}">
In UI, we need to create a Registration Page, with 3 Fields: UserName, Age, EmailId from Line: 26 to 54.
ResetButton – ICommad binded: ResetButtonClicked (from lines: 67 to 73)
If you click on the Reset button then all the 3 fields are cleared and buttons shall be disabled. as seen in the previous figure.
Conclusion
- Create and implement RelayCommands
- How to bind command in MVVM architecture (UI and Relaycommand.cs)
- How to use CanExecute & Execute methods and Event CanExecuteChanged in the RelayCommand
- By this Implementation, we have achieved dependency between modules by keeping Presentation (UI)= & BusinessLogic (ViewModel) loosely coupled (by Icommand implementation).
All Source code is in my Github
My next post describes Window Manager using MVVM Pattern
This post is part of “WPF step by step”