Tipps und Tricks zum MVVM-Pattern (Teil 2)
Kommentare

Keine RoutedCommands

Die ViewModel-Klassen stellen über Properties Daten und Commands bereit. Die in der WPF vorhandenen RoutedCommands stellen für das MVVM-Pattern jedoch eine fragwürdige Implementierung

Keine RoutedCommands

Die ViewModel-Klassen stellen über Properties Daten und Commands bereit. Die in der WPF vorhandenen RoutedCommands stellen für das MVVM-Pattern jedoch eine fragwürdige Implementierung dar. Abbildung 4 zeigt die Struktur bzw. Funktionsweise der RoutedCommands. Ein ICommandSource-Objekt, bspw. ein Button, enthält in der Command Property ein RoutedCommand. Wird dieser Button mit dem RoutedCommand geklickt, wird aufwärts im Element Tree nach CommandBinding-Instanzen gesucht, die dieses RoutedCommand ebenfalls besitzen und die Event Handler für die Events Executed und CanExecute definieren. Wie in Abbildung 4 jedoch zu sehen ist, werden die CommandBinding-Instanzen zur CommandBindings Property eines UIElements üblicherweise zu der eines Window-Elements hinzugefügt. Dies ist beim MVVM-Pattern problematisch, da die Event Handler für Executed und CanExecute in der View und nicht wie gewollt im ViewModel liegen. Die Event Handler ließen sich zwar im ViewModel definieren, müssten dann allerdings in der View mit einem CommandBinding „verdrahtet“ werden. Aus diesem Grund sind RoutedCommands für das MVVM-Pattern nicht wirklich geeignet. Stattdessen hat sich eine auf Delegates basierende ICommand-Implementierung durchgesetzt.

Abb. 4: Die Struktur der RoutedCommands in der WPF
Abb. 4: Die Struktur der RoutedCommands in der WPF
Das ActionCommand

Eine auf Delegates basierende ICommand-Implementierung bietet den Vorteil, dass die durch Delegates gekapselten Methoden für Execute und CanExecute sich direkt im ViewModel befinden können. In der Praxis hat sich das ActionCommand durchgesetzt.

Das ActionCommand gleicht dem DelegateCommand aus PRISM. PRISM ist ein Framework, um modulare WPF- und Silverlight-Anwendungen zu erstellen. Es enthält eine ICommand-Implementierung namens DelegateCommand, die weitestgehend dem folgenden ActionCommand entspricht und dieselbe Idee verfolgt: Ein Command bereitstellen, das für die Logik von Execute und CanExecute direkt Delegates entgegennimmt. Mehr zu PRISM (auch Composite WPF genannt) auf CodePlex.

Listing 2 zeigt eine mögliche Implementierung des auf Delegates basierenden ActionCommands. Der Konstruktor nimmt einen Action

Delegate entgegen, der in der Execute-Methode des Commands ausgeführt wird. Eine Konstruktorüberladung nimmt zudem einen Func Delegate entgegen, der in der CanExecute-Methode ausgeführt wird. Sehr interessant ist das CanExecuteChanged-Event. Es ist mit den für Events existierenden Accessoren add und remove ausgestattet. Wird ein Event Handler zu diesem Event hinzugefügt, wird der Event Handler direkt an das RequerySuggested-Event des CommandManagers der WPF angehängt. Dadurch wird das Auslösen des CanExecuteChanged-Events dem CommandManager der WPF überlassen, und man muss sich nicht selbst darum kümmern.
public class ActionCommand : ICommand
{
  private readonly Action _executeHandler;
  private readonly Func _canExecuteHandler;

  public ActionCommand(Action execute)
  {
    if (execute == null)
      throw new ArgumentNullException("Execute cannot be null");
    _executeHandler = execute;
  }

  public ActionCommand(Action execute, Func canExecute)
    : this(execute)
  {
    _canExecuteHandler = canExecute;
  }

  public event EventHandler CanExecuteChanged
  {
    add { CommandManager.RequerySuggested += value; }
    remove { CommandManager.RequerySuggested -= value; }
  }

  public void Execute(object parameter)
  {
    _executeHandler(parameter);
  }

  public bool CanExecute(object parameter)
  {
    if (_canExecuteHandler == null)
      return true;
    return _canExecuteHandler(parameter);
  }
}  

Mit der ActionCommand-Klasse aus Listing 2 lassen sich die Methoden für das Command einfach im ViewModel unterbringen. Die MainViewModel-Klasse des in diesem Artikel verwendeten Beispiels besitzt ein NextCommand, um in der Personenliste vorwärts zu navigieren (Listing 3). Im MainViewModel-Konstruktor wird dazu die Automation Property NextCommand mit einem ActionCommand initialisiert. Dabei werden die Methoden OnNextExecuted und OnNextCanExecute direkt angegeben. Sie befinden sich ebenfalls in der MainViewModel-Klasse. Löst ein Button das Command aus, wird die im MainViewModel enthaltene Methode OnNextCanExecute aufgerufen und das nächste Element bzw. die nächste Person selektiert.

public class MainViewModel : ViewModelBase
{
  public MainViewModel()
  {
    ...
    NextCommand = new ActionCommand(OnNextExecuted, OnNextCanExecute); 
    ...
  }
  ...
  public ICommand NextCommand { get; private set; }
  private void OnNextExecuted(object parameter)
  {
    _personsDefaultView.MoveCurrentToNext();
  }
  private bool OnNextCanExecute(object parameter)
  {
    return _personsDefaultView.CurrentPosition < _persons.Count - 1;
  }
}  
Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -