WPF Commands are an essential part of building clean, maintainable WPF applications, especially when using the MVVM pattern. They allow you to connect buttons, menus, keyboard shortcuts, and other controls to application logic without relying heavily on code-behind event handlers.
In this tutorial, you will learn WPF Commands step by step, starting with the ICommand interface and gradually moving to RelayCommand, RoutedCommand, and CommandParameter.
By the end, you will build a small WPF example where users can add, edit, and delete products using commands.
What You Will Learn About WPF Commands #
In this WPF Commands tutorial, we will cover:
- Understanding
ICommand - Creating your own
RelayCommand - Using
CanExecute - Passing data with
CommandParameter - Understanding
RoutedCommand - Using commands with keyboard shortcuts
- Building a complete command-based WPF example
The examples use C# and XAML and follow the MVVM approach.
Step 1: Understand WPF Commands #
What Is a Command? #
A command represents an action that a user can perform.
For example, an application might have commands such as:
- Save
- Delete
- Add
- Edit
- Search
- Refresh
- Copy
- Paste
- Submit
Instead of handling a button click directly:
<Button Content="Save"
Click="SaveButton_Click" />
you can use a command:
<Button Content="Save"
Command="{Binding SaveCommand}" />
The button no longer needs to know how the save operation works. It simply executes the command.
This separation is one of the main reasons WPF Commands work particularly well with MVVM.
How WPF Commands Work #
The basic flow looks like this:
User clicks Button
↓
Button executes Command
↓
ICommand.Execute()
↓
ViewModel method executes
↓
Application performs the operation
A command can also determine whether it is currently allowed to execute:
Button
↓
CanExecute()
↓
true → Button enabled
false → Button disabled
Step 2: Learn the ICommand Interface #
What Is ICommand? #
ICommand is the foundation of WPF Commands.
It is located in the System.Windows.Input namespace:
using System.Windows.Input;
The interface contains three important members:
public interface ICommand
{
event EventHandler? CanExecuteChanged;
bool CanExecute(object? parameter);
void Execute(object? parameter);
}
The three members have different responsibilities.
| Member | Purpose |
|---|---|
Execute() | Performs the command |
CanExecute() | Determines whether the command can run |
CanExecuteChanged | Notifies WPF that execution availability changed |
Create a Simple ICommand Implementation #
Let’s create a basic command class.
public class SaveCommand : ICommand
{
public event EventHandler? CanExecuteChanged;
public bool CanExecute(object? parameter)
{
return true;
}
public void Execute(object? parameter)
{
MessageBox.Show("Data saved!");
}
}
Now expose it from a ViewModel:
public class MainViewModel
{
public ICommand SaveCommand { get; }
public MainViewModel()
{
SaveCommand = new SaveCommand();
}
}
Then bind it to a button:
<Button Content="Save"
Command="{Binding SaveCommand}" />
When the user clicks the button, WPF calls:
SaveCommand.Execute(null);
The message box will appear.
Why Not Create a Class for Every Command? #
The previous example works, but imagine an application containing 50 operations.
You might end up creating:
SaveCommand.cs
DeleteCommand.cs
AddCommand.cs
EditCommand.cs
RefreshCommand.cs
SearchCommand.cs
...
This produces a lot of repetitive code.
This is where RelayCommand becomes useful.
Step 3: Create a RelayCommand for WPF Commands #
What Is RelayCommand? #
RelayCommand is a reusable implementation of ICommand.
Instead of creating a separate class for every operation, you provide methods that the command should call.
The basic idea is:
RelayCommand
↓
Execute → ViewModel method
↓
CanExecute → ViewModel method
Create the RelayCommand Class #
Create a file named:
RelayCommand.cs
Add the following code:
using System;
using System.Windows.Input;
public class RelayCommand : ICommand
{
private readonly Action _execute;
private readonly Func<bool>? _canExecute;
public RelayCommand(
Action execute,
Func<bool>? canExecute = null)
{
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object? parameter)
{
return _canExecute == null || _canExecute();
}
public void Execute(object? parameter)
{
_execute();
}
public event EventHandler? CanExecuteChanged;
public void RaiseCanExecuteChanged()
{
CanExecuteChanged?.Invoke(
this,
EventArgs.Empty);
}
}
Now you have a reusable command class.
Create a Save Command #
In your ViewModel:
public class MainViewModel
{
public ICommand SaveCommand { get; }
public MainViewModel()
{
SaveCommand = new RelayCommand(Save);
}
private void Save()
{
MessageBox.Show("Data saved successfully.");
}
}
Then bind it to XAML:
<Button Content="Save"
Command="{Binding SaveCommand}" />
You have now created your first practical WPF Command using RelayCommand.
Step 4: Use CanExecute with RelayCommand #
Why Use CanExecute? #
Sometimes a command should not always be available.
For example, a Save button might only work when there are unsaved changes.
Suppose your ViewModel contains:
private bool _hasChanges;
You can create the command like this:
SaveCommand = new RelayCommand(
Save,
CanSave);
Then implement:
private bool CanSave()
{
return _hasChanges;
}
When _hasChanges is false, the command cannot execute.
WPF can automatically disable the associated button.
Notify WPF When CanExecute Changes #
Suppose the user changes some data:
_hasChanges = true;
WPF needs to know that the result of CanExecute() has changed.
Call:
((RelayCommand)SaveCommand)
.RaiseCanExecuteChanged();
A complete example looks like this:
public class MainViewModel
{
private bool _hasChanges;
public ICommand SaveCommand { get; }
public MainViewModel()
{
SaveCommand = new RelayCommand(
Save,
CanSave);
}
private void Save()
{
MessageBox.Show("Saved.");
_hasChanges = false;
((RelayCommand)SaveCommand)
.RaiseCanExecuteChanged();
}
private bool CanSave()
{
return _hasChanges;
}
public void MakeChanges()
{
_hasChanges = true;
((RelayCommand)SaveCommand)
.RaiseCanExecuteChanged();
}
}
This pattern is very useful in real WPF applications.
Step 5: Pass Data Using CommandParameter #
What Is CommandParameter? #
CommandParameter allows you to send additional data to a command.
For example, suppose your application displays products:
Laptop
Keyboard
Mouse
Each product has its own Delete button.
Instead of creating three different commands, you can use one Delete command and pass the selected product as the parameter.
<Button Content="Delete"
Command="{Binding DeleteCommand}"
CommandParameter="{Binding}" />
The following:
CommandParameter="{Binding}"
means:
Pass the current data item to the command.
Create a Product Class #
public class Product
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
}
Create a Generic RelayCommand #
The previous RelayCommand does not accept a parameter. Let’s create a generic version.
public class RelayCommand<T> : ICommand
{
private readonly Action<T?> _execute;
private readonly Func<T?, bool>? _canExecute;
public RelayCommand(
Action<T?> execute,
Func<T?, bool>? canExecute = null)
{
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object? parameter)
{
return _canExecute == null ||
_canExecute((T?)parameter);
}
public void Execute(object? parameter)
{
_execute((T?)parameter);
}
public event EventHandler? CanExecuteChanged;
}
Now create the Delete command:
public ICommand DeleteCommand { get; }
public MainViewModel()
{
DeleteCommand =
new RelayCommand<Product>(DeleteProduct);
}
Implement the method:
private void DeleteProduct(Product? product)
{
if (product == null)
return;
// Delete product
}
The product passed through CommandParameter will be received by:
DeleteProduct(Product? product)
Step 6: Build a Complete Product Example #
Create the ViewModel #
Let’s create a small product management example.
using System.Collections.ObjectModel;
using System.Windows.Input;
public class MainViewModel
{
public ObservableCollection<Product> Products { get; }
public ICommand DeleteCommand { get; }
public MainViewModel()
{
Products = new ObservableCollection<Product>
{
new Product
{
Id = 1,
Name = "Laptop"
},
new Product
{
Id = 2,
Name = "Keyboard"
},
new Product
{
Id = 3,
Name = "Mouse"
}
};
DeleteCommand =
new RelayCommand<Product>(DeleteProduct);
}
private void DeleteProduct(Product? product)
{
if (product == null)
return;
Products.Remove(product);
}
}
Create the XAML #
Now display the products:
<ListBox ItemsSource="{Binding Products}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal"
Margin="5">
<TextBlock Text="{Binding Name}"
Width="150"
VerticalAlignment="Center" />
<Button Content="Delete"
Command="{Binding DataContext.DeleteCommand,
RelativeSource={
RelativeSource AncestorType=ListBox}}"
CommandParameter="{Binding}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
When the application runs, you will see something similar to:
Laptop [Delete]
Keyboard [Delete]
Mouse [Delete]
Clicking the Delete button for Keyboard sends that particular Product to the command.
The important part is:
CommandParameter="{Binding}"
This represents the current product.
Step 7: Understand RoutedCommand #
What Is RoutedCommand? #
RoutedCommand is another type of command provided by WPF.
Unlike a typical RelayCommand, a RoutedCommand participates in WPF’s routed command system.
The command can travel through the WPF element tree until an appropriate CommandBinding handles it.
WPF provides several built-in commands, including:
ApplicationCommands.Copy
ApplicationCommands.Cut
ApplicationCommands.Paste
ApplicationCommands.Undo
ApplicationCommands.Redo
Create a RoutedCommand #
You can create your own:
public static class AppCommands
{
public static readonly RoutedCommand Save =
new RoutedCommand();
}
You can then use it from XAML:
<Button Content="Save"
Command="{x:Static local:AppCommands.Save}" />
Add a CommandBinding #
The command needs a handler.
<Window.CommandBindings>
<CommandBinding
Command="{x:Static local:AppCommands.Save}"
Executed="Save_Executed"
CanExecute="Save_CanExecute" />
</Window.CommandBindings>
Then implement the handlers:
private void Save_Executed(
object sender,
ExecutedRoutedEventArgs e)
{
MessageBox.Show("Saved successfully.");
}
private void Save_CanExecute(
object sender,
CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
When Should You Use RoutedCommand? #
RoutedCommand is useful when you need WPF’s built-in command routing mechanism.
It can be particularly useful for:
- Menus
- Toolbars
- Keyboard shortcuts
- Text editing
- Application-wide UI commands
- Commands that need to locate their handler through the element tree
For an MVVM-based application, however, RelayCommand is often simpler for ViewModel operations.
Step 8: Compare RelayCommand and RoutedCommand #
RelayCommand vs RoutedCommand #
Both are useful WPF Commands, but they are designed for different scenarios.
| Feature | RelayCommand | RoutedCommand |
|---|---|---|
Implements ICommand | Yes | Yes |
| Common with MVVM | Yes | Less common |
| Uses ViewModel methods | Yes | Not directly |
Uses CommandBinding | No | Yes |
| Supports command routing | No | Yes |
| Supports command parameters | Yes | Yes |
| Good for CRUD operations | Yes | Usually not the first choice |
| Good for routed UI commands | Limited | Yes |
A good general rule is:
Use RelayCommand for ViewModel actions and RoutedCommand when you specifically need WPF command routing.
Step 9: Use WPF Commands with Keyboard Shortcuts #
Add a KeyBinding #
One of the useful features of WPF Commands is that the same command can be executed from multiple UI elements.
For example, create a Save command:
public ICommand SaveCommand { get; }
public MainViewModel()
{
SaveCommand = new RelayCommand(Save);
}
private void Save()
{
// Save data
}
Bind it to a button:
<Button Content="Save"
Command="{Binding SaveCommand}" />
Now add a keyboard shortcut:
<Window.InputBindings>
<KeyBinding
Key="S"
Modifiers="Control"
Command="{Binding SaveCommand}" />
</Window.InputBindings>
Now both:
Click Save
and:
Ctrl + S
execute the same command.
This avoids duplicating the save logic.
Step 10: Use WPF Commands with Menu Items #
Connect a MenuItem to a Command #
Commands are not limited to buttons.
You can use the same command with a menu:
<Menu>
<MenuItem Header="_File">
<MenuItem Header="_Save"
Command="{Binding SaveCommand}" />
</MenuItem>
</Menu>
You could therefore have:
File
└── Save
Toolbar
└── Save Button
Keyboard
└── Ctrl + S
All three can execute:
SaveCommand
This is one of the biggest advantages of WPF Commands.
Step 11: Use CommandParameter with Different Values #
Pass a String #
You can pass a string directly:
<Button Content="Edit"
Command="{Binding ActionCommand}"
CommandParameter="Edit" />
Pass a Number #
You can pass a number:
<Button Content="Open"
Command="{Binding OpenCommand}"
CommandParameter="10" />
Pass the Current Object #
For an item inside a list:
<Button Content="Delete"
Command="{Binding DataContext.DeleteCommand,
RelativeSource={
RelativeSource AncestorType=ListBox}}"
CommandParameter="{Binding}" />
Pass Multiple Values #
Sometimes a command needs more than one value. In that situation, you can use a small model, tuple, or MultiBinding depending on the application’s requirements.
For example, you could create:
public class ProductAction
{
public Product Product { get; set; } = null!;
public string Action { get; set; } = string.Empty;
}
Then pass an object containing all required information.
Step 12: WPF Commands in a Real MVVM Application #
Recommended Project Structure #
For a larger WPF application, you can organize commands like this:
MyWpfApp
│
├── Models
│ └── Product.cs
│
├── ViewModels
│ └── MainViewModel.cs
│
├── Commands
│ ├── RelayCommand.cs
│ └── RelayCommandT.cs
│
└── Views
└── MainWindow.xaml
This structure keeps command infrastructure separate from ViewModels and Views.
Typical Application Flow #
A real WPF application might work like this:
User
↓
WPF Control
↓
Command
↓
ViewModel
↓
Application Service
↓
Repository
↓
Database
For example, in a retail application:
Click Delete Product
↓
DeleteCommand
↓
Product passed as CommandParameter
↓
ViewModel
↓
Product Service
↓
Database
This makes the application easier to maintain as it grows.
Common WPF Commands Mistakes #
Putting Business Logic in Button Click Events #
Avoid placing large amounts of application logic in:
private void Button_Click(...)
{
// Lots of business logic
}
Prefer a command when the operation belongs to application or ViewModel logic.
Forgetting CommandParameter #
If your command needs to know which item the user selected, make sure you pass the object:
CommandParameter="{Binding}"
Without the parameter, the command may not know which item should be processed.
Forgetting CanExecuteChanged #
If the command’s availability changes dynamically, WPF needs to be notified.
For example:
RaiseCanExecuteChanged();
Without this notification, the button’s enabled state might not update when expected.
Creating Too Many Command Classes #
If every simple operation has its own class:
SaveCommand
DeleteCommand
EditCommand
RefreshCommand
SearchCommand
your project can become unnecessarily verbose.
A reusable RelayCommand can reduce this repetition.
WPF Commands Best Practices #
Use ICommand in ViewModel Properties #
Expose commands through ICommand:
public ICommand SaveCommand { get; }
This keeps the ViewModel dependent on the abstraction rather than a specific implementation.
Use RelayCommand for Common MVVM Operations #
For operations such as:
Add
Edit
Delete
Save
Refresh
Search
Submit
a RelayCommand is usually a clean solution.
Use CommandParameter for Item-Based Operations #
For lists and grids, pass the current object:
CommandParameter="{Binding}"
This allows one command to work with many items.
Use RoutedCommand for Routed UI Scenarios #
When command routing is important, consider RoutedCommand.
Do not use it simply because it exists; choose it when its routed behavior provides a real advantage.
Keep Commands Small #
A command should generally coordinate an action rather than contain a large amount of business logic.
For example:
private async Task Save()
{
await _productService.SaveAsync(Product);
}
The ViewModel can delegate complex operations to application or domain services.
Final WPF Commands Tutorial Project #
What We Built #
Throughout this tutorial, you learned how to create and use WPF Commands from the ground up.
You started with:
ICommand
Then created:
RelayCommand
After that, you learned how to control command availability using:
CanExecute()
You then passed data using:
CommandParameter="{Binding}"
You also learned about:
RoutedCommand
Finally, you connected commands to:
- Buttons
- Menu items
- Keyboard shortcuts
- List items
- Product operations
What You Should Remember #
The most important concepts are:
ICommand
↓
Defines the command contract
RelayCommand
↓
Reusable ICommand implementation
CanExecute
↓
Determines whether a command can execute
CommandParameter
↓
Passes data to the command
RoutedCommand
↓
Provides WPF routed command behavior
Once you understand these concepts, you can start replacing many traditional click-event handlers with clean, reusable WPF Commands.
The combination of WPF Commands + MVVM + data binding provides a powerful foundation for building professional WPF applications such as ERP systems, POS applications, inventory systems, dashboards, and business management software.
