WPF Commands
Introduction to Commands
In Windows Presentation Foundation (WPF), commands provide a way to abstract user input into actionable operations. They are a core part of the MVVM (Model-View-ViewModel) pattern, promoting separation of concerns and testability. Commands allow you to decouple the user interface element that triggers an action from the logic that performs the action.
Key benefits of using commands include:
- Decoupling: Separates UI from business logic.
- Reusability: Commands can be reused across different UI elements.
- Enabling/Disabling: Commands can dynamically manage their enabled/disabled state based on application context.
- Testability: Easier to unit test logic independent of the UI.
The ICommand
Interface
The foundation of WPF commands is the System.Windows.Input.ICommand
interface. This interface defines two essential methods and one event:
CanExecute(object parameter)
: Returns a boolean indicating whether the command can currently be executed.Execute(object parameter)
: Performs the command's action.CanExecuteChanged
: An event that is raised when the state of the command (whether it can be executed) changes.
Implementing ICommand
You can implement ICommand
yourself or use the built-in implementations provided by WPF.
public interface ICommand
{
bool CanExecute(object parameter);
void Execute(object parameter);
event EventHandler CanExecuteChanged;
}
Built-in Command Implementations
WPF provides several useful built-in command implementations:
RoutedCommand
RoutedCommand
s are commands that are routed through the element tree. They are often used when the command logic needs to interact with elements in the UI tree. They are typically associated with a gesture (like a key combination) and can be handled by any element along the route.
RoutedUICommand
Similar to RoutedCommand
, but specifically designed for common UI operations like Copy, Cut, Paste, Save, etc. These commands often have pre-defined gestures and display text.
DelegateCommand
(Not built-in, but common pattern)
While not directly part of the WPF framework, DelegateCommand
is a very common pattern used in MVVM scenarios, especially within ViewModel implementations. It wraps a delegate (or lambda expression) for the Execute
and CanExecute
logic, often using a callback for CanExecuteChanged
.
CommandManager
class plays a crucial role in coordinating command execution and managing the CanExecuteChanged
event for RoutedCommand
s.
Using Commands in XAML
Commands are typically bound to UI elements using the Command
property.
Binding to a Button
<Button Content="Save" Command="{Binding SaveCommand}" />
Binding to a MenuItem
<MenuItem Header="Edit">
<MenuItem Header="Copy" Command="{Binding CopyCommand}" />
<MenuItem Header="Paste" Command="{Binding PasteCommand}" />
</MenuItem>
Command Parameters
You can pass parameters to commands using the CommandParameter
property.
<Button Content="Delete Item" Command="{Binding DeleteCommand}" CommandParameter="{Binding SelectedItem}" />
ViewModel Implementation (MVVM Pattern)
In MVVM, commands are typically defined in the ViewModel. Here's an example using a simplified DelegateCommand
(often provided by libraries like MVVM Light or Prism, or implemented manually).
Manual DelegateCommand Implementation
public class DelegateCommand : ICommand
{
private readonly Action<object> _execute;
private readonly Predicate<object> _canExecute;
public event EventHandler CanExecuteChanged;
public DelegateCommand(Action<object> execute, Predicate<object> canExecute = null)
{
_execute = execute ?? throw new ArgumentNullException(nameof(execute));
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return _canExecute == null || _canExecute(parameter);
}
public void Execute(object parameter)
{
_execute(parameter);
}
public void RaiseCanExecuteChanged()
{
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
}
ViewModel Example
public class MyViewModel : INotifyPropertyChanged
{
private string _inputText;
public string InputText
{
get { return _inputText; }
set { _inputText = value; OnPropertyChanged(nameof(InputText)); }
}
public DelegateCommand SubmitCommand { get; private set; }
public DelegateCommand ClearCommand { get; private set; }
public MyViewModel()
{
SubmitCommand = new DelegateCommand(ExecuteSubmit, CanExecuteSubmit);
ClearCommand = new DelegateCommand(ExecuteClear);
}
private void ExecuteSubmit(object parameter)
{
// Perform submission logic with InputText
MessageBox.Show($"Submitted: {InputText}");
}
private bool CanExecuteSubmit(object parameter)
{
// Enable submit only if InputText is not empty
return !string.IsNullOrWhiteSpace(InputText);
}
private void ExecuteClear(object parameter)
{
InputText = string.Empty;
// Notify the submit command that its CanExecute state might have changed
SubmitCommand.RaiseCanExecuteChanged();
}
// INotifyPropertyChanged implementation...
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
InputText
) changes, you must ensure that the CanExecute
status of dependent commands (e.g., SubmitCommand
) is re-evaluated. Calling RaiseCanExecuteChanged()
on the command will trigger WPF to re-query its CanExecute
state.
Command Binding
CommandBinding
objects connect a command to a handler in the code-behind of a UI element. This is more commonly used with RoutedCommand
s and in code-behind scenarios rather than pure MVVM.
XAML
<Window.CommandBindings>
<CommandBinding Command="ApplicationCommands.Save" Executed="SaveCommand_Executed" CanExecute="SaveCommand_CanExecute" />
</Window.CommandBindings>
<Button Command="ApplicationCommands.Save" Content="Save" />
C# Code-Behind
private void SaveCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = IsDataDirty; // Assume IsDataDirty is a property
}
private void SaveCommand_Executed(object sender, ExecutedRoutedEventArgs e)
{
// Save logic
}
Summary
WPF Commands are a powerful mechanism for handling user interactions in a structured and maintainable way. They are essential for building responsive and testable applications, especially when following the MVVM pattern. By understanding the ICommand
interface and leveraging built-in or custom command implementations, you can significantly improve the architecture of your WPF applications.