ICommand Interface In MVVM

Introduction


MVVM - Model - View & ViewModel

Model is the DBAccess layer, View is Presentation & ViewModel is the BusinessLogic layer.

ICommand is an interface between the Presentation & the BusinessLogic layer.

Whenever any button is pressed on the 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, so ICommand was introduced. It is a way to trigger ViewModel when action is performed on View.

Let's start the cooking process, we need our main ingredient, RelayCommand.

RelayCommand is going to be inherited from ICommand.

ICommand has one event: EventHandler CanExecuteChanged and 2 methods: Execute & CanExecute
  1. An Event
    EventHandler CanExecuteChanged: This event runs asynchronously in the background, It keeps track if logic in the method CanExecute() has changed.

  2. CanExecute method
    CanExecute plays a vital role. Take a scenario, let's say you have a user registration page & 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.

  3. Execute method
    The execution of an Execute method depends on what CanExecute returns.

    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 is going to need 2 delegates.

One is for Execute which will be Action delegate because this method is not gonna return a value.

CanExecute will be a fun delegate because this method always has to returns something because everything else depends on it.

If you want to learn about Action, Fun & Predicate delegates, you can visit Delegates Explained.

Our final implementation of RelayCommand will look like this:
  1. using System;  
  2. using System.Windows.Input;  
  3.   
  4. namespace A  
  5. {  
  6.   
  7.     public class RelayCommand : ICommand  
  8.     {  
  9.         Action<object> _execute;  
  10.         Func<objectbool> _canExecute;  
  11.   
  12.   
  13.         public RelayCommand(Action<object> execute, Func<objectbool> canExecute)  
  14.         {  
  15.             _execute = execute;  
  16.             _canExecute = canExecute;  
  17.         }  
  18.   
  19.         public bool CanExecute(object parameter)  
  20.         {  
  21.             if (_canExecute != null)  
  22.             {  
  23.                 return _canExecute(parameter);  
  24.             }  
  25.             else  
  26.             {  
  27.                 return false;  
  28.             }  
  29.         }  
  30.   
  31.         public event EventHandler CanExecuteChanged {  
  32.             add  
  33.             {  
  34.                 CommandManager.RequerySuggested += value;  
  35.             }  
  36.             remove  
  37.             {  
  38.                 CommandManager.RequerySuggested -= value;  
  39.             }  
  40.         }  
  41.   
  42.         public void Execute(object parameter)  
  43.         {  
  44.             _execute(parameter);  
  45.         }  
  46.     }  

Now we need the UI, let's create a Registration Page.

The flow of data:
 
3 Fields:
  • UserName
  • Age
  • EmailId
RegisterButton - ICommad binded: RegisterButtonClicked
ResetButton - ICommad binded: ResetButtonClicked

Validations

RegisterButton will only be enabled if the user enters a UserName

ResetButoon will only be enabled if any of the TextBoxes have some value to reset.
 
1st MainWindow.xaml
 
See how RegisterButton & ResetButton are bound:
  1. <Button x:Name="ButtonRegister"            
  2.                 Height="20"            
  3.                 Width="100"            
  4.                 Content="Register"            
  5.                 HorizontalAlignment="Center"            
  6.                 Margin="20 10 0 0"          
  7.                 Command="{Binding RegisterButtonClicked}"/>    
  8.             
  9.         <Button x:Name="ButtonReset"            
  10.                 Height="20"            
  11.                 Width="100"            
  12.                 Content="Reset"            
  13.                 HorizontalAlignment="Center"            
  14.                 Margin="20 10 0 0"          
  15.                 Command="{Binding ResetButtonClicked}"/>     
Complete View
  1. <Window x:Class="A.MainWindow"    
  2.         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"    
  3.         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"    
  4.         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"    
  5.         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"    
  6.         mc:Ignorable="d"    
  7.         Title="MainWindow" Height="200" Width="320">    
  8.     <Window.Resources>    
  9.         <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>    
  10.     </Window.Resources>    
  11.     <Grid HorizontalAlignment="Center">    
  12.         <Grid.RowDefinitions>    
  13.             <RowDefinition Height="Auto"/>    
  14.             <RowDefinition Height="Auto"/>    
  15.             <RowDefinition Height="Auto"/>    
  16.             <RowDefinition Height="Auto"/>    
  17.             <RowDefinition Height="Auto"/>    
  18.             <RowDefinition Height="5*"/>    
  19.         </Grid.RowDefinitions>    
  20.         <Grid.ColumnDefinitions>    
  21.             <ColumnDefinition Width="Auto"/>    
  22.             <ColumnDefinition Width="Auto"/>    
  23.         </Grid.ColumnDefinitions>    
  24.         <Label x:Name="LabelUserName"            
  25.                Content="User Name:"        
  26.                Margin="0 10 0 0"/>    
  27.         <Label x:Name="LabelAge"             
  28.                Content="Age:"            
  29.                Grid.Row="1"/>    
  30.         <Label x:Name="LabelEmailId"             
  31.                Content="Email:"            
  32.                Grid.Row="2"/>    
  33.     
  34.         <TextBox x:Name="TextBoxUserName"          
  35.                  Text="{Binding UserName}"        
  36.                  Height="20"            
  37.                  Width="150"           
  38.                  Margin="0 10 0 0"         
  39.                  Grid.Column="1"/>    
  40.         <TextBox  x:Name="TextBoxAge"    
  41.                  Text="{Binding Age}"     
  42.                  Height="20"            
  43.                  Width="150"            
  44.                  Grid.Column="1"            
  45.                  Grid.Row="1"/>    
  46.             
  47.         <TextBox x:Name="TextBoxEmail"     
  48.                  Text="{Binding EmailId}"     
  49.                  Height="20"            
  50.                  Width="150"            
  51.                  Grid.Column="1"            
  52.                  Grid.Row="2"/>    
  53.         <StackPanel x:Name="StackPanelButtons"     
  54.                     Orientation="Horizontal"     
  55.                     Grid.ColumnSpan="2"    
  56.                     Grid.Row="3" >    
  57.         <Button x:Name="ButtonRegister"            
  58.                 Height="20"            
  59.                 Width="100"            
  60.                 Content="Register"            
  61.                 HorizontalAlignment="Center"            
  62.                 Margin="20 10 0 0"          
  63.                 Command="{Binding RegisterButtonClicked}"/>    
  64.             
  65.         <Button x:Name="ButtonReset"            
  66.                 Height="20"            
  67.                 Width="100"            
  68.                 Content="Reset"            
  69.                 HorizontalAlignment="Center"            
  70.                 Margin="20 10 0 0"          
  71.                 Command="{Binding ResetButtonClicked}"/>    
  72.         </StackPanel>    
  73.         <TextBlock x:Name="TextBlockMessage"        
  74.                    HorizontalAlignment="Center"        
  75.                    Margin="20 8 0 0"          
  76.                    Grid.Row="4"        
  77.                    Grid.ColumnSpan="2">    
  78.             <TextBlock.Style>    
  79.                 <Style TargetType="TextBlock">    
  80.                     <Setter Property="Text"    
  81.                                     Value="Enter details to register!">    
  82.                     </Setter>    
  83.                     <Style.Triggers>    
  84.                         <DataTrigger Binding="{Binding IsButtonClicked}" Value="True">    
  85.                             <Setter Property="Text"    
  86.                                     Value="{Binding UserName, StringFormat='User: {0} is successfully registered!'}">    
  87.                             </Setter>    
  88.                         </DataTrigger>    
  89.                     </Style.Triggers>    
  90.                 </Style>    
  91.             </TextBlock.Style>    
  92.         </TextBlock>    
  93.     </Grid>    
  94. </Window>     
Now MainWindowViewModel.

String properties
 
UserName, Age & EmailId represents 2 TextBoxes in UI

Based on IsButtonClicked's boolean value, our XAML will decide what message to show (style applied on TextBlock x:Name="TextBlockMessage") command for both buttons:
  1. public ICommand RegisterButtonClicked { getset; }  
  2. public ICommand ResetButtonClicked { getset; } 
Constructor where commands are initialized:
  1. #region Constructor  
  2.       public MainWindowViewModel()  
  3.       {  
  4.           RegisterButtonClicked = new RelayCommand(RegisterUser, CanUserRegister);  
  5.           ResetButtonClicked = new RelayCommand(ResetPage, CanResetPage);  
  6.       }  
  7. #endregion 
Lastly, all the respective methods associated with commands.
  1. using Prism.Commands;  
  2. using Prism.Mvvm;  
  3. using System.Windows.Input;  
  4.   
  5. namespace WPFAPP 
  6. {  
  7.     class MainWindowViewModel : BindableBase  
  8.     {  
  9.         #region Properties  
  10.         private string _userName;  
  11.         public string UserName  
  12.         {  
  13.             get { return _userName; }  
  14.             set { SetProperty(ref _userName, value); }  
  15.         }  
  16.   
  17.         private string _age;  
  18.         public string Age  
  19.         {  
  20.             get { return _age; }  
  21.             set { SetProperty(ref _age, value); }  
  22.         }  
  23.   
  24.         private string _emailId;  
  25.         public string EmailId  
  26.         {  
  27.             get { return _emailId; }  
  28.             set { SetProperty(ref _emailId, value); }  
  29.         }  
  30.   
  31.         private bool _isButtonClicked;  
  32.   
  33.         public bool IsButtonClicked  
  34.         {  
  35.             get { return _isButtonClicked; }  
  36.             set { SetProperty(ref _isButtonClicked , value); }  
  37.         }  
  38.  
  39.         #endregion  
  40.  
  41.         #region ICommands  
  42.         public ICommand RegisterButtonClicked { getset; }  
  43.         public ICommand ResetButtonClicked { getset; }  
  44.         #endregion  
  45.  
  46.         #region Constructor  
  47.         public MainWindowViewModel()  
  48.         {  
  49.             RegisterButtonClicked = new RelayCommand(RegisterUser, CanUserRegister);  
  50.             ResetButtonClicked = new RelayCommand(ResetPage, CanResetPage);  
  51.         }  
  52.         #endregion  
  53.  
  54.         #region Event Methods  
  55.         private void RegisterUser(object value)  
  56.         {  
  57.             IsButtonClicked = true;  
  58.         }  
  59.   
  60.         private bool CanUserRegister(object value)  
  61.         {  
  62.              
  63.             if (string.IsNullOrEmpty(UserName))  
  64.             {  
  65.                 return false;  
  66.             }  
  67.             else  
  68.             {  
  69.                 return true;  
  70.             }  
  71.         }  
  72.   
  73.         private void ResetPage(object value)  
  74.         {  
  75.             IsButtonClicked = false;  
  76.             UserName = Age = EmailId = "";  
  77.         }  
  78.   
  79.         private bool CanResetPage(object value)  
  80.         {  
  81.             if (string.IsNullOrEmpty(UserName)  
  82.                 || string.IsNullOrEmpty(EmailId)  
  83.                 || string.IsNullOrEmpty(Age))  
  84.             {  
  85.                 return false;  
  86.             }  
  87.             else  
  88.             {  
  89.                 return true;  
  90.             }  
  91.         }  
  92.         #endregion  
  93.     }  

Very well, now let's check out the output:
 
 

Conclusion


In this article:
  • We learned how to create RelayCommands
  • How to bind them in MVVM architecture
  • How to use CanExecute & Execute methods
  • We achieved dependency between modules by keeping Presentation & BusinessLogic loosely coupled
We now support loosely coupled applications, all the very best to you.
 
I believe that now you will be able to successfully implement a WPF application using commands.