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
- An 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 & 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.
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:
- using System;
- using System.Windows.Input;
-
- namespace A
- {
-
- 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);
- }
- }
- }
Now we need the UI, let's create a Registration Page.
The flow of data:
3 Fields:
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:
- <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}"/>
Complete View
- <Window x:Class="A.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"
- mc:Ignorable="d"
- Title="MainWindow" Height="200" Width="320">
- <Window.Resources>
- <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
- </Window.Resources>
- <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>
- </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:
- public ICommand RegisterButtonClicked { get; set; }
- public ICommand ResetButtonClicked { get; set; }
Constructor where commands are initialized:
- #region Constructor
- public MainWindowViewModel()
- {
- RegisterButtonClicked = new RelayCommand(RegisterUser, CanUserRegister);
- ResetButtonClicked = new RelayCommand(ResetPage, CanResetPage);
- }
- #endregion
Lastly, all the respective methods associated with commands.
- using Prism.Commands;
- using Prism.Mvvm;
- using System.Windows.Input;
-
- namespace WPFAPP
- {
- class MainWindowViewModel : 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 MainWindowViewModel()
- {
- 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
- }
- }
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.