mvvm-relaycommand

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

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  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.

ICommand has one event: EventHandler CanExecuteChanged and 2 methods: Execute & CanExecute
  1. 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 ( 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.
  3. 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.

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 Func, Action And Predicate Delegates In C#
Create a WPF application project and call  it WPF_MVVM_ICommand in Visual studio then Add two folder: Command and ViewModel, then under Command folder create a class call it RelayCommand.cs  and under ViewModel folder create another class  and name it ViewModel.cs as seen in  the following figure:

mvvm-relaycommand-3.png

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.

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:
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.

RegisterButton – ICommad binded: RegisterButtonClicked  (from lines: 59 to 65<)
ResetButton – ICommad binded: ResetButtonClicked (from lines: 67 to 73)
DataTrigger Binding IsButtonClicked (Line: 86) to show the TextBlock content: User: with Username is successfully registered!’
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.
Step 4: Run the application then you see  the following in the UI:
mvvm-relaycommand-4.png
As we see the Register and Reset buttons are disabled (Inactive)
Now write a User Name  e.g. mehzan then the Register and Reset buttons shall be enabled as seen in the following figure.
mvvm-relaycommand-5.png

 

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

In this Post I have describe and implement the followings:
  •  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).
By Icommand implementation, support loosely coupled applications, all the very best to you.

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”

Back to home page

 

Leave a Reply

Your email address will not be published. Required fields are marked *