DelegateCommand class
Simplifies commands for ViewModels by the MVVM pattern. Provides an ICommand implementation which relays the Execute and CanExecute method to the specified delegates.
The problem
Besides data binding between the View and the ViewModel layer of the MVVM pattern, which represents rather ongoing states, Views must also trigger actions in the ViewModel, that is events that occur at a point in time. The .NET framework provides the ICommand
interface for that task, it executes a command and tells whether it is currently available.
In order to implement that interface, one must create a separate class for each command that only contains the logic of that one command. In extensive user interfaces that makes a lot of classes, some with just a few lines of code. Moreover, these classes cannot access the internal state of the ViewModel, which is what they should work on most of the time.
The solution
To simplify that there’s the DelegateCommand
class, sometimes also called RelayCommand
. This class implements the ICommand
interface but does not contain the command logic itself but instead only a delegate that runs it. This delegate, a method, is specified in the DelegateCommand
constructor. This method is normally located in the same ViewModel class that also provides the command. Many short methods are thereby also in the class they should work with, e. g. modify their properties that in turn are displayed on the UI through data binding.
Besides delegates for the two methods Execute
and CanExecute
, each with an object
parameter, this class also features overloads that don’t require a parameter, and additional helper methods. TryExecute
for instance invokes Execute
only if CanExecute
returns true. As an alternative to calling the CanExecute
method, the IsEnabled
property can be set. The RaiseCanExecuteChanged
method raises the same-named event to re-evaluate the command’s availability; RaiseCanExecuteChangedAsync
defers the event. The static property DelegateCommand.
provides a command that is always disabled and does nothing.
Compatibility:
Example
The following sample class shows a ViewModel with commands:
{
// Declaration of all commands for this ViewModel as public properties
public DelegateCommand OpenFileCommand { get; private set; }
public DelegateCommand SaveCommand { get; private set; }
public DelegateCommand SaveAsCommand { get; private set; }
public DelegateCommand PrintCommand { get; private set; }
public DelegateCommand QuitCommand { get; private set; }
// Constructor
public MainViewModel()
{
// Initialisation of the commands, each with a handler method and optionally
// an implementation for CanExecute
OpenFileCommand = new DelegateCommand(OnOpenFile);
SaveCommand = new DelegateCommand(OnSave, CanSave);
SaveAsCommand = new DelegateCommand(OnSaveAs, CanSave);
// Printing is not yet supported, so it should always be disabled
PrintCommand = DelegateCommand.Disabled;
QuitCommand = new DelegateCommand(OnQuit);
}
// Method that is called when a file should be loaded
private void OnOpenFile()
{
LoadedFile = AskForFile();
// The Save commands are now available, their executability should now
// be re-evaluated to update UI elements
SaveCommand.RaiseCanExecuteChanged();
SaveAsCommand.RaiseCanExecuteChanged();
// Instead of implementing a CanExecute method a command can also be
// activated and deactivated by a property
QuitCommand.IsEnabled = false;
}
// Method that determines whether files can be saved.
// In this example, both save commands share the same method.
private bool CanSave()
{
// Saving is possible, when a file has been loaded
return LoadedFile != null;
}
private void OnSave()
{
WriteFile(LoadedFile);
}
private void OnSaveAs()
{
LoadedFile.FileName = newFileName;
// The save command is reused and executed, when it can be executed
// right now. Otherwise, nothing happens.
SaveCommand.TryExecute();
}
private void OnQuit()
{
if (LoadedFile.IsModified)
{
AskUserToSave();
}
else
{
CloseApplication();
}
}
}
Download
DelegateCommand.cs10.3 KiBSource code of the DelegateCommand class
Licence and terms of use
Copying and distribution of this file, with or without modification, are permitted provided the copyright notice and this notice are preserved. This file is offered as-is, without any warranty. (GNU All-Permissive licence)
Statistic data
- Created on 2012-07-09, updated on 2016-08-28.
- Ca. 170 lines of code, estimated development costs: 170 - 680 €