官术网_书友最值得收藏!

Introducing the ICommand interface

When it comes to button clicks in WPF and MVVM, instead of handling the well-known Click event, we typically use some form of command that implements the ICommand interface. Let's take a look at an example of a basic standard command:

using System; 
using System.Windows.Forms; 
using System.Windows.Input; 
 
public class TestCommand : ICommand 
{ 
  public event EventHandler CanExecuteChanged; 
 
  public void Execute(object parameter) 
  { 
    MessageBox.Show("You executed a command"); 
  } 
 
  public bool CanExecute(object parameter) 
  { 
    return true; 
  } 
}
Please note that in this book, we will display code with two-space tabs, instead of the more commonly used four-space tabs, in order to enable more characters of each code snippet to fit onto each line.

We can see that it has an Execute method, where the functionality that the command provides is performed. The CanExecute method is called by the CommandManager at various points over time, when it believes that the output value may have changed. We'll cover this in more detail later, but basically, raising the CanExecuteChanged event is one of the ways to trigger the CommandManager to do this. The output of the CanExecute method specifies whether the Execute method can be called or not.

You can imagine how cumbersome it would be if we had to create one of these classes for every action that we needed to implement. Furthermore, there is no context of where the command was called from other than the single command parameter. This means that if we wanted the command to add an item into a collection, we would have to put both the collection and the item to add into another object so that they could both be passed through the single input parameter.

When using MVVM, rather than implementing the commands in the standard way, we tend to use a single, reusable implementation that allows us to handle actions with standard methods directly in the View Model. This enables us to use commands without having to create a separate class for each one. There are a number of variations of this command, but its simplest form is shown here:

using System; 
using System.Windows.Input; 
 
public class ActionCommand : ICommand 
{ 
  private readonly Action<object> action; 
  private readonly Predicate<object> canExecute; 
 
  public ActionCommand(Action<object> action) : this(action, null) { } 
 
  public ActionCommand(Action<object> action, 
    Predicate<object> canExecute) 
  { 
    this.action = action; 
    this.canExecute = canExecute; 
  } 
 
  public event EventHandler CanExecuteChanged 
  { 
    add { CommandManager.RequerySuggested += value; } 
    remove { CommandManager.RequerySuggested -= value; } 
  } 
 
  public bool CanExecute(object parameter) 
  { 
    return canExecute == null ? true : canExecute(parameter); 
  } 
 
  public void Execute(object parameter) 
  { 
    action(parameter); 
  } 
} 

The action parameter of type Action<object> will hold the reference to the method that will be called when the command is executed and the object generic parameter relates to the optionally used command parameter. The canExecute parameter of type Predicate<object> will hold the reference to the method that will be called to verify whether the command can be executed or not and its object generic parameter relates to the optionally used command parameter again.

The CanExecuteChanged event should be raised whenever the canExecute parameter value changes. It is typically handled by command sources, such as the Button control, to set their IsEnabled property value appropriately. When a command source receives a notification that this event has been raised, it will call the ICommand.CanExecute method to check the new value. Therefore, when a command can execute, its data bound control will be enabled and when it can't, its data bound control will be disabled.

The CommandManager.RequerySuggested event will be raised when the CommandManager detects a change in the UI that could reflect on whether a command could execute or not. For example, this could be due to a user interaction, such as the selection of an item in a collection or some other change in focus. Therefore, connecting one to the other seems to be a logical thing to do. In fact, an example of this is actually found in the source code of the .NET RoutedCommand class.

This command class is typically used in the View Model classes, as shown in the following example, where the command functionality comes from the Save method and the bool return value of the CanSave method determines whether the command can execute or not:

public ICommand SaveCommand 
{ 
  get { return new ActionCommand(action => Save(), 
    canExecute => CanSave()); } 
} 

A safer way to ensure that the command is never called by code when the CanExecute condition is not satisfied would be to make this alteration; however, note that the CommandManager class will always perform this check before calling any commands anyway:

public void Execute(object parameter) 
{ 
  if (CanExecute(parameter)) action(parameter); 
} 

Full credit for this custom command should go to Josh Smith, as his RelayCommand class was the first implementation like this that I came across, although there are several variations to be found online. The beauty of this particular implementation should not be underestimated. Not only is it simple, elegant, and saves us from writing large amounts of code, but it also makes testing our functionality much easier, as our command code can now be defined right in our View Models.

We'll look at this ActionCommand again and in more detail in Chapter 3, Writing Custom Application Frameworks, but for now, let's move on to the next method of communication.

主站蜘蛛池模板: 沂水县| 沙河市| 云阳县| 云安县| 贵州省| 监利县| 靖宇县| 砚山县| 塔河县| 米林县| 娄烦县| 淳化县| 平泉县| 天门市| 萨嘎县| 伊吾县| 昌乐县| 五河县| 吉水县| 洱源县| 崇信县| 神木县| 邮箱| 邮箱| 广汉市| 永城市| 綦江县| 长寿区| 阿拉善左旗| 岢岚县| 察哈| 稷山县| 巴中市| 南溪县| 西和县| 贵德县| 财经| 永春县| 巴马| 高要市| 乐至县|