DelegateCommand-Klasse

Vereinfacht Commands für ViewModels nach dem MVVM-Muster. Stellt eine ICommand-Implementierung bereit, die die Execute- und CanExecute-Methoden an Delegaten weiterleitet.

Das Problem

Neben der Datenbindung zwischen der View- und der ViewModel-Ebene im MVVM-Muster, die eher andauernde Zustände darstellt, müssen von der View aus auch Aktionen im ViewModel ausgelöst werden, also Ereignisse, die zu einem Zeitpunkt auftreten. Das .NET-Framework stellt für diesen Zweck die ICommand-Schnittstelle bereit, die einen Befehl ausführt und darüber Auskunft gibt, ob er gerade ausgeführt werden kann.

Um diese Schnittstelle zu bedienen, müsste für jedes Command eine separate Klasse erstellt werden, die nur die Logik dieses einen Befehls enthält. In umfangreichen Oberflächen sind das sehr viele Klassen, manche mit nur wenigen Zeilen Code. Außerdem können diese Klassen nicht auf den internen Zustand des ViewModels zugreifen, den sie doch meist verändern sollen.

Die Lösung

Um das zu vereinfachen, gibt es die DelegateCommand-Klasse, manchmal auch RelayCommand genannt. Diese Klasse implementiert die ICommand-Schnittstelle, enthält aber nicht selbst die Befehlslogik, sondern nur einen Delegaten, der sie ausführt. Dieser Delegat, eine Methode, wird im Konstruktor von DelegateCommand angegeben. Die Methode befindet sich dabei i. d. R. in derselben ViewModel-Klasse, die auch das Command bereitstellt. Viele kurze Methoden befinden sich somit auch gleich in der Klasse, mit der sie arbeiten sollen, z. B. deren Eigenschaften sie verändern, die dann wiederum per Datenbindung auf der Oberfläche angezeigt werden.

Neben Delegaten für die beiden Methoden Execute und CanExecute mit jeweils einem object-Parameter gibt es in dieser Klasse auch Überladungen, die keinen Parameter annehmen, und weitere Hilfsmethoden. TryExecute z. B. ruft nur dann Execute auf, wenn CanExecute true zurückgibt. Alternativ zum Aufruf der CanExecute-Methode lässt sich auch die Eigenschaft IsEnabled setzen. Die RaiseCanExecuteChanged-Methode löst das gleichnamige Ereignis aus, mit dem die Verfügbarkeit des Commands neu abgefragt wird; RaiseCanExecuteChangedAsync verzögert das Ereignis. Die statische Eigenschaft DelegateCommand.Disabled stellt ein Command bereit, das generell nicht verfügbar ist und nichts tut.

Kompatibilität: .NET Ab Version 4.0

Beispiel

Die folgende Beispielklasse zeigt ein ViewModel mit Commands:

public class MainViewModel : ViewModelBase
{
    // Deklaration aller Commands für dieses ViewModel als öffentliche Eigenschaften
    public DelegateCommand OpenFileCommand { get; private set; }
    public DelegateCommand SaveCommand { get; private set; }
    public DelegateCommand SaveAsCommand { get; private set; }
    public DelegateCommand PrintCommand { get; private set; }
    public DelegateCommand QuitCommand { get; private set; }
   
    // Konstruktor
    public MainViewModel()
    {
        // Initialisierung der Commands, jeweils mit Handler-Methode und optional
        // einer Implementierung für CanExecute
        OpenFileCommand = new DelegateCommand(OnOpenFile);
        SaveCommand = new DelegateCommand(OnSave, CanSave);
        SaveAsCommand = new DelegateCommand(OnSaveAs, CanSave);
        // Drucken wird noch nicht unterstützt, soll also immer deaktiviert sein
        PrintCommand = DelegateCommand.Disabled;
        QuitCommand = new DelegateCommand(OnQuit);
    }
   
    // Methode, die aufgerufen wird, wenn eine Datei geladen werden soll
    private void OnOpenFile()
    {
        LoadedFile = AskForFile();
       
        // Die Speichern-Befehle stehen jetzt zur Verfügung, ihre Ausführbarkeit
        // soll jetzt neu abgefragt werden, um UI-Elemente zu aktivieren
        SaveCommand.RaiseCanExecuteChanged();
        SaveAsCommand.RaiseCanExecuteChanged();
       
        // Statt eine CanExecute-Methode zu implementieren, kann ein Befehl auch
        // per Eigenschaft aktiviert und deaktiviert werden
        QuitCommand.IsEnabled = false;
    }

    // Methode, die ermittelt, ob gespeichert werden kann. Wird hier für beide
    // Speichern-Befehle gleichermaßen verwendet.
    private bool CanSave()
    {
        // Speichern geht, wenn eine Datei geladen wurde
        return LoadedFile != null;
    }
   
    private void OnSave()
    {
        WriteFile(LoadedFile);
    }
   
    private void OnSaveAs()
    {
        LoadedFile.FileName = newFileName;
        // Der Speichern-Befehl wird hier wiederverwendet und ausgeführt,
        // wenn er gerade ausführbar ist. Sonst passiert nichts.
        SaveCommand.TryExecute();
    }
   
    private void OnQuit()
    {
        if (LoadedFile.IsModified)
        {
            AskUserToSave();
        }
        else
        {
            CloseApplication();
        }
    }
}

Download

DelegateCommand.cs10,3 KiBQuelltext der DelegateCommand-Klasse

Lizenz und Nutzungsbedingungen

Vervielfältigung und Weiterverbreitung dieser Datei, verändert oder unverändert, sind gestattet, vorausgesetzt die Urheberrechtsangabe und dieser Hinweis bleiben erhalten. Diese Datei wird wie vorliegend ohne jegliche Garantie oder Gewährleistung angeboten. (GNU All-Permissive-Lizenz)

Statistische Daten

  • Erstellt am 2012-07-09, aktualisiert am 2016-08-28.
  • Ca. 170 Codezeilen, geschätzte Ent­wick­lungs­kos­ten: 170 - 680 €