Using RelayCommand / ICommand to handle events in WPF and MVVM

A step-by-step guide to using the RelayCommand class (based on ICommand) as means of handling user-initiated events in WPF / MVVM / XAML. 1. Create a new WPF application command1 2. Add the View Model class Right click your project folder and select Add > New Item > Class. Name your class MainWindowViewModel.cs: command2 The MainWindowViewModel class will be used implement a very simple function to display a message box when it receives the incoming event. Add the functions and code to MainWindowViewModel.cs as follows: [code language="csharp"] using System.Windows; using System.Windows.Input; namespace Command { public class MainWindowViewModel { private ICommand _command; public ICommand Command { get { return _command ?? (_command = new RelayCommand( x => { DoStuff(); })); } } private static void DoStuff() { MessageBox.Show("Responding to button click event..."); } } } [/code] 3. Add classes to implement RelayCommand, and event handling As before, just right click your project folder and select Add > New Item > Class to add the following classes: RelayCommand.cs [code language="csharp"] using System; using System.Windows.Input; namespace Command { public class RelayCommand<T> : ICommand { private readonly Predicate<T> _canExecute; private readonly Action<T> _execute; public RelayCommand(Action<T> execute) : this(execute, null) { _execute = execute; } public RelayCommand(Action<T> execute, Predicate<T> canExecute) { if (execute == null) { throw new ArgumentNullException("execute"); } _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 { add { CommandManager.RequerySuggested += value; } remove { CommandManager.RequerySuggested -= value; } } } public class RelayCommand : ICommand { private readonly Predicate<object> _canExecute; private readonly Action<object> _execute; public RelayCommand(Action<object> execute) : this(execute, null) { _execute = execute; } public RelayCommand(Action<object> execute, Predicate<object> canExecute) { if (execute == null) { throw new ArgumentNullException("execute"); } _execute = execute; _canExecute = canExecute; } public bool CanExecute(object parameter) { return _canExecute == null || _canExecute(parameter); } public void Execute(object parameter) { _execute(parameter); } // Ensures WPF commanding infrastructure asks all RelayCommand objects whether their // associated views should be enabled whenever a command is invoked public event EventHandler CanExecuteChanged { add { CommandManager.RequerySuggested += value; CanExecuteChangedInternal += value; } remove { CommandManager.RequerySuggested -= value; CanExecuteChangedInternal -= value; } } private event EventHandler CanExecuteChangedInternal; public void RaiseCanExecuteChanged() { CanExecuteChangedInternal.Raise(this); } } } [/code] EventRaiser.cs [code language="csharp"] using System; namespace Command { public static class EventRaiser { public static void Raise(this EventHandler handler, object sender) { handler?.Invoke(sender, EventArgs.Empty); } public static void Raise<T>(this EventHandler<EventArgs<T>> handler, object sender, T value) { handler?.Invoke(sender, new EventArgs<T>(value)); } public static void Raise<T>(this EventHandler<T> handler, object sender, T value) where T : EventArgs { handler?.Invoke(sender, value); } public static void Raise<T>(this EventHandler<EventArgs<T>> handler, object sender, EventArgs<T> value) { handler?.Invoke(sender, value); } } } [/code] EventArgs.cs [code language="csharp"] using System; namespace Command { public class EventArgs<T> : EventArgs { public EventArgs(T value) { Value = value; } public T Value { get; private set; } } } [/code] 4. Update your main window - add a button Modify the MainWindow.xaml file to add the button. Notice that in the button XAML we include the Command="{Binding Command}" to bind to command contained in the MainWindowViewModel command. Also it is necessary to specify the DataContext. [code language="xml"] <Window x:Class="Command.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" xmlns:local="clr-namespace:Command" Title="MainWindow" Height="150" Width="225"> <Window.DataContext> <local:MainWindowViewModel /> </Window.DataContext> <Grid> <Button Content="Button" Command="{Binding Command}" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="10,10,0,0" Width="75"> </Button> </Grid> </Window> [/code] 5. Try it! That is all there is to it. Run the program and click the button to verify that the ICommand is run and displays the message dialog as shown: command3 command4

Comments

Popular posts from this blog

Using the Supervisor Controller Pattern to access View controls in MVVM

Getting started with client-server applications in C++

How to send an e-mail via Google SMTP using C#