WPF databinding to methods encapulated in an ICommand
Databinding in WPF allows binding the Command property to methods encapulated in an ICommand. By creating an ICommand object to hold an event function, the Command value can bind to the event function as an ICommand.
The goal of Model-View-ViewModel is to have zero code in the code behind of a WPF Control Instead, everything the WPF Control does happens using databinding.
While this article will show you how to do this, you be left a little fuzzy as to understanding of the implementation. It may take some time and research to fully understand everything this is doing. Understand that methods can be objects, and this is a process to turn a method object into an ICommand so it can be using in WPF for databinding.
Preparation and Prereqs
You should have Visual Studio 2008/2010.
In Visual Studio, create a new WPF Application project and give it a name.
Step 1 – Creating an new class that inherits from ICommand
- In your new project in Visual Studio, add a new class called RelayCommand.
Note: It can be named anything, but since that is the name used by Microsoft when discussing MVVM, I will use the same name. - Change the using statements to implement the following : System, System.Diagnostic, System.Windows.Input
- Make the new RelayComand class public.
- Make the new RelayCommand class implement ICommand.
using System; using System.Windows.Input; namespace WpfDataBindingToICommand { public class RelayCommand : ICommand { #region Constructors public RelayCommand() { } #endregion } }
- Right-click on the ICommand text and choose Implement Interface | Implement Interface. This adds the following code to the bottom of your class.
#region ICommand Members public bool CanExecute(object parameter) { throw new NotImplementedException(); } public event EventHandler CanExecuteChanged; public void Execute(object parameter) { throw new NotImplementedException(); } #endregion
- Create two member variables or fields that we will use to hep use inside the ICommand interface functions.
1. Action<object>
2. Predicate<object>#region Member Variables readonly Action<object> _ActionToExecute; readonly Predicate<object> __ActionCanExecute; #endregion
- Implement the CanExecute(object parameter) function.
public bool CanExecute(object parameter) { return __ActionCanExecute== null ? true : __ActionCanExecute(parameter); }
- Implement the EventHandler CanExecuteChanged. In doing this the MVVM experts used the CommandManager, which might be worth reading about.
public event EventHandler CanExecuteChanged { add { CommandManager.RequerySuggested += value; } remove { CommandManager.RequerySuggested -= value; } }
- Implement the Execute(object parameter) function.
public void Execute(object parameter) { _ActionToExecute(parameter); }
- Create constructors that allow us to initialize the object by passing in the Action
The final class looks as follows:
using System; using System.Windows.Input; namespace WpfDataBindingToICommand { /// <summary> /// This RelayCommand object is used to encapsulate function logic into an oject that inherits ICommand. /// </summary> public class RelayCommand : ICommand { #region Member Variables readonly Action<object> _ActionToExecute; readonly Predicate<object> _ActionCanExecute; #endregion #region Constructors /// <summary> /// This creates a new RelayCommand. /// </summary> /// <param name="inActionToExecute">This is the logic of the actin to execute. This objects is usually a method that returns void.</param> public RelayCommand(Action<object> inActionToExecute) : this(inActionToExecute, null) { } /// <summary> /// This creates a new RelayCommand. /// </summary> /// <param name="inActionToExecute">This is the logic of the actin to execute. This objects is usually a method that returns void.</param> /// <param name="inActionCanExecute">This is the logic for whether the action can execute.</param> public RelayCommand(Action<object> inActionToExecute, Predicate<object> inActionCanExecute) { if (inActionToExecute == null) throw new ArgumentNullException("execute"); _ActionToExecute = inActionToExecute; _ActionCanExecute = inActionCanExecute; } #endregion #region ICommand Members public bool CanExecute(object parameter) { return _ActionCanExecute == null ? true : _ActionCanExecute(parameter); } public event EventHandler CanExecuteChanged { add { CommandManager.RequerySuggested += value; } remove { CommandManager.RequerySuggested -= value; } } public void Execute(object parameter) { _ActionToExecute(parameter); } #endregion } }
Step 2 – Creating a ViewModelBase abstract base class
This object is used to create common logic for all objects that will be using in Binding. This object will implement INotifyPropertyChanged so it only has to be implemented once.
- Create a new class named ViewModelBase.
- Change the using statements to implement the following : System, System.CompenentModel
- Make the new ViewModelBase class public and abstract.
- Make the new ViewModelBase class implement INotifyPropertyChanged.
- Right-click on the INotifyPropertyChanged text and choose Implement Interface | Implement Interface. This adds the following code to the bottom of your class. Yes, it is just a one line event handler object.
#region INotifyPropertyChanged Members public event PropertyChangedEventHandler PropertyChanged; #endregion
- Create a function called NotifyPropertyChanged to help implement the object. Make sure it has a permission level of at least protected.
#region Functions protected void NotifyPropertyChanged(String inPropertyName) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(inPropertyName)); } } #endregion
- Make the new ViewModelBase class public and abstract.
The final object looks as follows:
using System; using System.ComponentModel; namespace WpfDataBindingToICommand { public abstract class ViewModelBase : INotifyPropertyChanged { #region Constructors public ViewModelBase() { } #endregion #region Functions protected void NotifyPropertyChanged(String inPropertyName) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(inPropertyName)); } } #endregion #region INotifyPropertyChanged Members public event PropertyChangedEventHandler PropertyChanged; #endregion } }
Note: You may also want to implement IDisposable.
Step 3 – Creating the ViewModel and Model
We are going to have the ViewModel and business in the same object for this example, but sometimes you will have a separate ViewModel object that represents your data/business.
- Create a new class named SampleViewModel.
- Change the using statements to implement the following : System, System.Windows, System.Windows.Input
- Make the new SampleViewModel class public.
- Make the new SampleViewModel class inherit ViewModelBase.
using System; using System.Windows; using System.Windows.Input; namespace WpfDataBindingToICommand { public class SampleViewModel : ViewModelBase { #region Constructors public SampleViewModel() { } #endregion } }
- Create a string field and property and make sure to have the property’s set function call NotifyPropertyChanged.
public class SampleViewModel : ViewModelBase { string _Message = "Hello. This is the default message."; public string Message { get { return _Message; } set { _Message = value; NotifyPropertyChanged("Message"); } } }
- Create a simple function to show a MessageBox.
public void ShowMessage(String inMessage) { MessageBox.Show(inMessage); }
- Create an ICommand field and property. Make sure the property returns a RelayCommand object that references the ShowMessage method. This is a read only property.
RelayCommand _ShowMessageCommand; public ICommand ShowMessageCommand { get { if (_ShowMessageCommand == null) { _ShowMessageCommand = new RelayCommand(param => this.ShowMessage(Message)); } return _ShowMessageCommand; } }
Note: Notice that in order to pass the ShowMessage method, instead of the return value of the function, into the RelayCommand objectwhich is void anyway, the param => syntax is used.
The final SampleViewModel looks as follows.
using System; using System.Windows; using System.Windows.Input; namespace WpfDataBindingToICommand { public class SampleViewModel : ViewModelBase { #region Member Variables string _Message = "Hello. This is the default message."; RelayCommand _ShowMessageCommand; #endregion #region Constructors public SampleViewModel() { } #endregion #region Properties public string Message { get { return _Message; } set { _Message = value; NotifyPropertyChanged("Message"); } } public ICommand ShowMessageCommand { get { if (_ShowMessageCommand == null) { _ShowMessageCommand = new RelayCommand(param => this.ShowMessage(Message)); } return _ShowMessageCommand; } } #endregion #region Functions public void ShowMessage(String inMessage) { MessageBox.Show(inMessage); } #endregion #region Enums #endregion } }
Step 4 – Using Databinding to Bind an ICommand to a WPF Control
Ok, so lets modify the XAML of the default MainWindow.xaml code that was auto-created with the project. We will keep it simple and have a text box and a button to pop up the message.
Note: For this simple program all the work we did to implement databinding for binding events to methods seems like an absurd burden. However, for large applications, this design will lead to a better way to manage your code. It will decouple your GUI from your code, making future refactoring of the GUI much easier. This also improves the ability to make minor changes to the GUI. It also makes the code more sustainable and more easily tested. Unit tests are more effective as the GUI layer is not required and most functions are in the business layer.
- Create a reference to the current namespace.
<window x:Class="WpfDataBindingToICommand.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WpfDataBindingToICommand" Title="MainWindow" Height="350" Width="525">
- Add a SampleViewModel StaticResource.
<window.Resources> <local:SampleViewModel x:Key="Sample" /> </window.Resources>
- Set the DataContext of the Grid to the SampleViewModel StaticResource.
<grid.RowDefinitions> <rowDefinition Height="*" /> <rowDefinition Height="50" /> </grid.RowDefinitions>
- Add two rows to the Grid.
<grid DataContext="{StaticResource ResourceKey=Sample}">
- Add a TextBox and remove the sizing and alignments. Set Margin to 5. Bind the Text property to Message.
<textBox Text="{Binding Message}" Name="textBoxMessage" Margin="5"/>
- Add a button. Set HorizontalAlignment to Right. Set the Width to Auto. Set Margin to 5. Bind the Command property to ShowMessageCommand.
<button Command="{Binding ShowMessageCommand}" Content="ShowMessage" Grid.Row="1" Height="23" Name="buttonShowMessage" HorizontalAlignment="Right" Width="Auto" Margin="5"/>
You are done. The final XAML is as follows:
<window x:Class="WpfDataBindingToICommand.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WpfDataBindingToICommand" Title="MainWindow" Height="350" Width="525"> <window.Resources> <local:SampleViewModel x:Key="Sample" /> </window.Resources> <grid DataContext="{StaticResource ResourceKey=Sample}"> <grid.RowDefinitions> <rowDefinition Height="*" /> <rowDefinition Height="50" /> </grid.RowDefinitions> <textBox Text="{Binding Message}" Name="textBoxMessage" Margin="5"/> <button Command="{Binding ShowMessageCommand}" Content="ShowMessage" Grid.Row="1" Height="23" Name="buttonShowMessage" HorizontalAlignment="Right" Width="Auto" Margin="5"/> </grid> </window>
Notice that we never touched the code behind of MainWindow. The GUI and the code are as decoupled as possible. Not event the event functions are needed in the code behind. This decoupling or GUI and code is our goal.
Resources
WPF Apps With The Model-View-ViewModel Design Pattern
Understanding Routed Events and Commands In WPF
Copyright ® Rhyous.com – Linking to this page is allowed without permission and as many as ten lines of this page can be used along with this link. Any other use of this page is allowed only by permission of Rhyous.com.