Silverlight 4 - Make Commanding use lesser code

In my previous post about using Silverlight 4 Commanding, I got a question why I didn’t use delegates etc instead of creating a lot of ICommand classes. In this post I’m going to show how you can reduce the number of ICommand classes.

I have created a class with the name Command, this class implements the ICommand interface and uses two constructor arguments of a delegate type, in this case the Predicate<T> and Action<T>. I use the Predicate<T> for the ICommand.CanExecute because it takes one argument and returns a bool (I could have used the Func<T, TResult>), and I use the Action<T> for the ICommand.Execute, Action<T> doesn’t return anything and takes one argument.

Here is the implementation of the Command class:

public class Command : ICommand
{
    public event EventHandler CanExecuteChanged;

    Predicate<Object> _canExecute = null;
    Action<Object> _executeAction = null;

    public Command(Predicate<Object> canExecute, Action<object> executeAction)
    {
        _canExecute = canExecute;
        _executeAction = executeAction;
    }


    public bool CanExecute(object parameter)
    {
        if (_canExecute != null)
            return _canExecute(parameter);

        return true;
    }

    public void UpdateCanExecuteState()
    {
       if (CanExecuteChanged != null)
            CanExecuteChanged(this, new EventArgs());
}
    public void Execute(object parameter)
    {
        if (_executeAction != null)
            _executeAction(parameter);



UpdateCanExecuteState(); } }


Because the ICommand’s CanExecute and Execute method takes one argument of type ojbect (the value comes from the ButtonBase class’s CommandParamter) I decided to still use it, so the T in Preditcate<T> and Actiton<T> is of type object. In this example I also added the code to trigger the CanExecuteChanged event after the Execute is done. The reason I did that was because the CanExecute can be based on the result of the Execute. You can trigger the CanExecuteChanged event when you want the UI to check the CanExecute method. The CanExecute will normally take place when the UI is loaded, and before the Execute method is executed.

To specify a Command in a ViewModel with the Command class I only need to return an instance of the Command where I pass two lambda expressions to the constructor. Here is an example:

public class CommandingViewModel : INotifyPropertyChanged
{
  public event PropertyChangedEventHandler PropertyChanged;

  private string _myTextBox;
  private Command _saveCommand = null;

  public string MyTextBox
  {
     get { return _myTextBox; }
     set
     {
         if (_myTextBox != value)
         {
             _myTextBox = value;

              if (PropertyChanged != null)
                  PropertyChanged(this, new PropertyChangedEventArgs("MyTextBox"));
          }
      }
   }


   public ICommand SaveCommand
   {
      get
      {
if(_saveCommand == null) _saveCommand = new Command ( p => string.IsNullOrEmpty(MyTextBox), p => MyTextBox = "Save Button Pressed" );




return _saveCommand; } }
}

The first lambda expression passed to the first argument of the Command class will return true or false to enable or disable for example a Button where the Command is used. The second lambda expression is the code that will be executed when the Button is pressed (when the Command is executed). The “p” in the expressions will hold the CommandParameter result. Here is the View I used for the example above:

<UserControl
    xmlns:view="clr-namespace:Commanding.ViewModel"
    ...>

    <UserControl.Resources>
        <view:CommandingViewModel x:Name="myView"/>
    </UserControl.Resources>
    
    <Grid x:Name="LayoutRoot" DataContext="{StaticResource myView}">

        <StackPanel VerticalAlignment="Center">

            <TextBox Height="50" Width="200" Text="{Binding MyTextBox}"/>
            <Button Content="Save" Height="50" Width="100" CommandParameter="1"  Command="{Binding SaveCommand}"/>

        </StackPanel>
       
        
    </Grid>
</UserControl>


As you can see, with a solution where we pass a lambda expression, we can reduce the number classes used and also make sure the Command code stays within the ViewModel.

If you want to know when I publish new blog posts, then you can follow me on twitter: http://www.twitter.com/fredrikn

10 Comments

  • the above approach is fine and how i have my project setup, but what about when you want to add a button to a cell of a datagrid row, then fire a command on your viewmodel?

    the issue is for me... i have the datagrid itemssource set to a observablecollection on my viewmodel, but this means i cannot reach the ICommand / Command on my viewmodel, as my grid is now hooked up to the observablecollection as a data source....

    so I can set the DataContext of my button to the ViewModel, to get access to my ICommands, but then I am not able to use the value from a different cell in my datagrid row as a parameter...???

    anyone have a solution for this issue?

  • Is there any difference between this approach and the RelayCommand widely used in most MVVM wpf app ?


  • @Steveairsoft:
    Don't know haven't looked at them.. but I guess the RelayCommand uses something similar..

  • here is the MvvmLight implementation : http://mvvmlight.codeplex.com/sourcecontrol/changeset/view/35938?projectName=mvvmlight#750822


  • @Steveairsoft:
    Thanks, the RelayCommand ignores the CommandParameter, so that will differ.. but I never use the CommandParameter, so I could have ignored it also..


  • @Thomas Claudius Huber:
    :) The reason I didn't use the Func&lt;T,TResult&gt; was only based on the discription of the Predicate&lt;T&gt; and the Action&lt;T&gt; ;)
    Predicate: "Represents the method that defines a set of criteria and determines whether the specified object meets those criteria."
    In this case the criteria is if the Command can be executed or not.
    Action: "Encapsulates a method that takes a single parameter and does not return a value."
    The Action shouldn't return anything, only execute.
    I only wanted to show the concept.

  • Hi,

    I have a few questions based on you blog. I tried to figure it out myself but couldn't.

    In the ViewModel the SaveCommand Predicate checks if it can be executed. In your example only when MyTextBox is null or empty. But when I cleared the TextBox in the View the Save button stayed disabled. Do I miss something here? I expected that when it is emptied or null again the button would be enabled again.

    In a previous answer you said that you could ignore CommandParameter. I am curious what possible use there could be (within Silverlight 4) for CommandParameter. Do you know any?

    Thank you forany help.
    Perry


  • @Perry:
    You need to trigger the CanExecuteChanged event. I haven't added a method for it in my example, in my example I used it in the Execute.. You need to manually call the method which will trigger the CanExecuteChanged event.. When the event is trigged, the CanExecute method will be called and can enable or disable a button based on the result.

  • Thank you Fredrik. Now it is clear for me. And suddenly so simple :)

  • Hi , Can somebody tell me if i am not giving DataContext="{StaticResource myView}" in View.
    and i am calling the View from MainPage.xmal like this.
    CommandingViewModel mVM = new CommandingViewModel();
    MinerDataView.DataContext = mVM;

    Will my SaveCommand Binding will work ?

    In my project its not Working.
    Regards
    Debashish.

Comments have been disabled for this content.