Commands

Commands provide a means for encapsulating some logic, enabling that logic to be reused. One of the key benefits of commands, however, is that some controls allow you to bind to and execute them in XAML. In other words, not only can you bind to data in Silverlight but you can also bind to operations! For example, a ViewModel might expose a Save command, that a button in the View can bind to and execute when the button is clicked.

images Note Commands are commonly used in conjunction with the MVVM design pattern to allow a ViewModel to expose operations to a View.

Creating a Command

To create a command class, it must implement the ICommand interface, as shown in Figure 13-6.

images

Figure 11-8. The ICommand interface

The CanExecute property specifies whether or not the command can be executed. When the command is bound to a button, the button will be enabled or disabled accordingly. In order for the button's enabled status to update when the property updates, the CanExecuteChanged event on the command should be raised. Any logic to be executed when the command is executed will go in the Execute method.

images Note It's a common problem to forget to raise the CanExecuteChanged event after changing the value of the CanExecute property. If you find that your button is disabled despite having updated the value of the CanExecute property, this is most likely the source of the issue.

To create a command, create a new class and implement the ICommand interface. The following code demonstrates a simple command that will display a message box when it is executed:

using System;
using System.Windows;
using System.Windows.Input;

namespace Chapter11Sample
{
    public class TestCommand : ICommand
    {
        public bool CanExecute(object parameter)
        {
            return true;
        }

        public event EventHandler CanExecuteChanged;

        public void Execute(object parameter)
        {
            MessageBox.Show("Command executed!");
        }
    }
}

Binding to a Command

A number of controls in Silverlight have a pair of properties named Command and CommandParameter—namely the Button, HyperlinkButton, ToggleButton, and RepeatButton controls. Each of these controls can bind its Command property to a command and will execute the bound command when it is clicked.

images Note The ContextMenu control from the Silverlight Toolkit can also have its menu items bound to commands. Unfortunately, no other controls have support for commands in Silverlight, but you can still use the InvokeCommandAction action from the Expression Blend Interactivity library to invoke a command in response to any event that a control raises (such as in response to an item in a ListBox control being selected).

The following code demonstrates creating the TestCommand command shown in the previous section as a resource and binding to it:

<UserControl x:Class="Chapter11Sample.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:Chapter11Sample">

    <UserControl.Resources>
        <local:TestCommand x:Key="testCommand" />
    </UserControl.Resources>

    <Grid x:Name="LayoutRoot" Background="White">
        <Button Content="Test Command"
                Command="{Binding Source={StaticResource testCommand}}" />
    </Grid>
</UserControl>

Passing a Parameter to a Command

You may have noticed that the Execute method of the command has a parameter of type object. Any value that you assign or bind to the control's CommandParameter property will be passed into both the CanExecute method and the Execute method when the command is executed.

For example, you can bind this property to a property on another control (such as the SelectedItem property on a ListBox control), and the value of that control's property will be passed to the Execute method of the command as its parameter. The following XAML demonstrates this scenario:

<UserControl x:Class="Chapter11Sample.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:Chapter11Sample">

    <UserControl.Resources>
        <local:TestCommand x:Key="testCommand" />
    </UserControl.Resources>

    <Grid x:Name="LayoutRoot" Background="White" Width="200" Height="300">
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="40" />
        </Grid.RowDefinitions>

        <ListBox Name="ProductList" />

        <Button Content="Test Command" Grid.Row="1"
                Command="{Binding Source={StaticResource testCommand}}"
                CommandParameter="{Binding SelectedItem, ElementName=ProductList}" />
    </Grid>
</UserControl>

The DelegateCommand

You've seen how you can encapsulate a piece of logic within a command, but you may not always want to have to put commands in their own class. For example, you want to expose some logic from a ViewModel class to a View as a command that can be bound to but retain the logic within the ViewModel class since it won't be used anywhere else in the application. In this scenario, you should consider implementing the command as a DelegateCommand. A DelegateCommand is an ICommand implementation that allows you to delegate the implementation of the command to a method in your ViewModel class. You can then expose the DelegateCommand object from your ViewModel class as a property, allowing you to easily expose operations from ViewModels as commands when using the MVVM design pattern. The following code is the source for the DelegateCommand class:

using System;
using System.Windows.Input;

namespace SimpleMVVM
{
    public class DelegateCommand : ICommand
    {
        private Func<object, bool> canExecute;
        private Action<object> executeAction;
        private bool canExecuteCache;

        public DelegateCommand(Action<object> executeAction, Func<object, bool> canExecute)
        {
            this.executeAction = executeAction;
            this.canExecute = canExecute;
        }

        #region ICommand Members
        public bool CanExecute(object parameter)
        {
            bool temp = canExecute(parameter);

            if (canExecuteCache != temp)
            {
                canExecuteCache = temp;
                if (CanExecuteChanged != null)
                {
                    CanExecuteChanged(this, new EventArgs());
                }
            }

            return canExecuteCache;
        }

        public event EventHandler CanExecuteChanged;

        public void Execute(object parameter)
        {
            executeAction(parameter);
        }
        #endregion
    }
}

In your ViewModel class, you will need to define two methods: one returning a Boolean value indicating whether the command can be executed, and the other that will be called when the command is actually executed. If we were to implement the TestCommand command demonstrated earlier as a DelegateCommand, you'd start by adding the following two methods to your ViewModel class:

private bool CanTest(object param)
{
    return true;
}
public void Test(object param)
{
    MessageBox.Show("Command executed!");
}

images Note When you implement methods in a ViewModel class to be wrapped in a DelegateCommand like this, the methods must conform to the same signature demonstrated here (i.e., have the same parameters and return types).

The next step is to create an instance of the DelegateCommand class, passing the two methods to its constructor, and returning the resulting object as a property from your ViewModel class:

public ICommand TestCommand
{
  get { return new DelegateCommand(Test, CanTest); }
}

A Button control in the View can have its Command property bound to the TestCommand property on the ViewModel. When the button is clicked, the command will be executed, resulting in it calling the Test method in the ViewModel.

images Note Another popular alternative to the DelegateCommand is the RelayCommand. An implementation of the RelayCommand command can be obtained from the MVVM Light Toolkit (one of the popular MVVM frameworks for Silverlight, which you will find listed in the “Frameworks section” in Chapter 13).

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset