20.5 Commands 

Viele Operationen müssen Sie in nahezu jeder Anwendung neu implementieren: das Kopieren von Daten in die Zwischenablage, das Ausschneiden von markiertem Text, das Speichern von Änderungen usw. WPF vereinfacht die Codierungsarbeit dadurch, dass Ihnen eine große Zahl bereits vorgefertigter Operationen zur Verfügung gestellt werden. Entwickeln Sie beispielsweise ein Menü mit dem Hauptmenüpunkt Bearbeiten und den untergeordneten Elementen Kopieren, Ausschneiden und Einfügen, brauchen Sie nur auf die vordefinierten Kommandos zurückzugreifen.
20.5.1 Vordefinierte Commands 

Die von WPF bereitgestellten Commands lassen sich in sechs Kategorien unterteilen. Jede Kategorie wird durch eine Klasse beschrieben, die wir uns nun anschauen wollen:
- AnnotationService (im Namespace System.Windows.Annotations)
- ApplicationCommands (im Namespace System.Windows.Input)
- ComponentCommands (im Namespace System.Windows.Input)
- EditingCommands (im Namespace System.Windows.Documents)
- MediaCommands (im Namespace System.Windows.Input)
- NavigationCommands (im Namespace System.Windows.Input)
Die einzelnen Commands werden in den Klassen als statische Eigenschaften bereitgestellt. Sie sind alle vom Typ RoutedUICommand.
Ihnen hier alle Commands vorzustellen, würde den Rahmen dieses Kapitels sprengen. Stattdessen möchte ich Ihnen einige wichtige Commands aus ApplicationCommands vorstellen, damit Sie ein Gefühl dafür bekommen, welche Möglichkeiten Ihnen mit Commands eröffnet werden.
Command | Beschreibung |
Close |
Stellt den Close-Befehl dar. |
ContextMenu |
Stellt den ContextMenu-Befehl dar. |
Copy |
Stellt den Copy-Befehl dar. |
Cut |
Stellt den Cut-Befehl dar. |
Delete |
Stellt den Delete-Befehl dar. |
Find |
Stellt den Find-Befehl dar. |
Open |
Stellt den Open-Befehl dar. |
Paste |
Stellt den Paste-Befehl dar. |
Stellt den Print-Befehl dar. |
|
Redo |
Stellt den Redo-Befehl dar. |
Save |
Stellt den Save-Befehl dar. |
Undo |
Stellt den Undo-Befehl dar. |
20.5.2 Beispielanwendung 

An einem Beispiel wollen wir uns nun ansehen, wie Commands eingesetzt werden. Dazu wird in einem Window eine Menüleiste bereitgestellt, die die Menüpunkte Datei und Bearbeiten enthält. Der Fensterbereich wird durch zwei TextBoxen beansprucht.
// --------------------------------------------------------- // Beispiel: ...\Kapitel 20\ApplicationCommandDemo // --------------------------------------------------------- <Window ...> <DockPanel> <Menu DockPanel.Dock="Top" Name="mnuMenu"> <MenuItem Header="_Datei"> <MenuItem Command="ApplicationCommands.New" /> <MenuItem Command="ApplicationCommands.Open"> <MenuItem.Icon> <Image Source="Images/openHS.png" /> </MenuItem.Icon> </MenuItem> <Separator /> <MenuItem Command="ApplicationCommands.Save"> <MenuItem.Icon> <Image Source="Images/saveHS.png" /> </MenuItem.Icon> </MenuItem> <MenuItem Command="ApplicationCommands.SaveAs" /> <Separator /> <MenuItem Header="_Senden an"> <MenuItem Header="_Mail" /> <MenuItem Header="_Desktop" /> </MenuItem> <MenuItem Header="_Beenden" /> </MenuItem> <MenuItem Header="_Bearbeiten"> <MenuItem Command="ApplicationCommands.Copy" /> <MenuItem Command="ApplicationCommands.Cut" /> <MenuItem Command="ApplicationCommands.Paste" /> </MenuItem> </Menu> <StackPanel> <TextBox Name="txtOben" Height="100"></TextBox> <TextBox Name="txtUnten" Height="100"></TextBox> </StackPanel> </DockPanel> </Window>
Sie werden feststellen, dass die Menüpunkte nun nicht nur automatisch beschriftet werden, sondern dass darüber hinaus auch die allgemein üblichen Shortcuts eingeblendet werden.
Wenn Sie die Anwendung starten, funktioniert das Ausschneiden, Kopieren und Einfügen bereits einwandfrei. Wenn Sie Text in der oberen TextBox markieren und anschließend die untere fokussieren, können Sie den Text, der sich in der Zwischenablage befindet, in die untere TextBox einfügen. Kann eine Operation nicht ausgeführt werden, zum Beispiel weil sich keine Daten in der Zwischenablage befinden oder in der fokussierten TextBox keine Zeichen markiert sind, werden die entsprechenden Menüpunkte deaktiviert – und das, obwohl Sie keine Zeile Code geschrieben haben.
20.5.3 Commando-Ziel festlegen 

Per Vorgabe ist das Ziel eines Kommandos das in dem Moment aktive Steuerelement. Bei Bedarf können Sie aber auch ein anderes Ziel festlegen. Neben dem Command-Attribut muss dann auch noch das Attribut CommandTarget angegeben werden. Dabei muss wieder die Binding-Syntax verwendet werden, wie das folgende Beispiel zeigt:
... <MenuItem Header="_Bearbeiten"> <MenuItem Command="ApplicationCommands.Copy"/> <MenuItem Command="ApplicationCommands.Cut" /> <MenuItem Command="ApplicationCommands.Paste" CommandTarget="{Binding ElementName=txtUnten}" /> </MenuItem> ...
20.5.4 Commands an Ereignisse binden 

Vielleicht ist Ihnen im Beispiel aufgefallen, dass die Untermenüpunkte von Datei alle deaktiviert sind. Das liegt daran, dass sich hinter den Kommandos kein Code verbirgt. Das ist auch nicht verwunderlich, denn es gibt keinen allgemeingültigen Code, der alle Umstände einer öffnenden Operation abdeckt, wie es beispielsweise beim Kopieren der Fall ist.
Hier hilft nur die Bindung eines Commands an einen Ereignishandler. Dabei kommt ein Objekt vom Typ CommandBinding ins Spiel, das mit Executed und CanExecute zwei Ereignisse bereitstellt. Executed führt die gewünschte Operation aus, und mit CanExecute wird optionalerweise geprüft, ob bestimmte Bedingungen erfüllt sind, damit der Befehl ausgeführt werden kann. Dazu stellt der zweite Parameter im Ereignishandler, der vom Typ CanExecuteRoutedEventArgs ist, mit CanExecute eine Eigenschaft bereit, der entweder true oder false übergeben werden muss. Mit CanExecute=false wird das Kommando deaktiviert.
Ein CommandBinding-Objekt muss mit einem Command verbunden werden. Dazu dient die gleichnamige Eigenschaft Command. Mit dem Attribut Excecuted oder CanExecute wird der Ereignishandler festgelegt, der auf das Kommando reagieren soll. Üblicherweise werden alle CommandBinding-Objekte im Bereich von Window angegeben.
Mit diesen Kenntnissen wollen wir die Anwendung so ergänzen, dass das komplette Menü mit Kommandos ausgestattet ist. Dabei sollen die Menüpunkte Speichern und Speichern als nur dann aktiviert werden, wenn die obere der beiden Textboxen nicht leer ist.
// --------------------------------------------------------- // Beispiel: ...\Kapitel 20\ApplicationCommands // --------------------------------------------------------- <Window ...> <Window.CommandBindings> <CommandBinding Command="ApplicationCommands.New" Executed="New_Executed" /> <CommandBinding Command="ApplicationCommands.Open" Executed="Open_Executed" /> <CommandBinding Command="ApplicationCommands.Save" Executed="Save_Executed" CanExecute="Save_CanExecute"/> <CommandBinding Command="ApplicationCommands.SaveAs" Executed="SaveAs_Executed" CanExecute="Save_CanExecute" /> </Window.CommandBindings> <DockPanel> ... </Window>
In der Code-Behind-Datei wird der erforderliche Code für die Operationen hinterlegt. Allerdings enthält er in unserem Beispiel nur die Anzeige von Nachrichtenfenstern. Im Ernstfall müssen Sie hier natürlich die gewünschte Logik hinterlegen.
public partial class Window1 : Window { public Window1() { InitializeComponent() } private void New_Executed(object sender, ExecutedRoutedEventArgs e) { MessageBox.Show("Menüpunkt 'Neu'"); } private void Open_Executed(object sender, ExecutedRoutedEventArgs e) { MessageBox.Show("Menüpunkt 'Öffnen'"); } private void Save_Executed(object sender, ExecutedRoutedEventArgs e) { MessageBox.Show("Menüpunkt 'Speichern'"); } private void SaveAs_Executed(object sender, ExecutedRoutedEventArgs e) { MessageBox.Show("Menüpunkt 'Speichern als'"); } private void Save_CanExecute(object sender, CanExecuteRoutedEventArgs e) { if (txtOben.Text == "") e.CanExecute = false; else e.CanExecute = true; } }
20.5.5 Commands programmieren 

Commands können auch per Code bereitgestellt werden. Der entsprechende Ereignishandler wird mit der bekannten Notation an das Ereignis gebunden:
public Window1() { InitializeComponent(); CommandBinding cmdSave = new CommandBinding(ApplicationCommands.Save); cmdSave.Executed += new ExecutedRoutedEventHandler(cmdSave_Executed); } void cmdSave_Executed(object sender, ExecutedRoutedEventArgs e) { // ... }
Sie können auch die Execute-Methode eines Commands aufrufen, um das Ereignis auszulösen. Execute definiert zwei Parameter. Der erste erwartet ein benutzerdefiniertes Objekt, der zweite erwartet die Angabe des Ziels des Kommandos:
private void button1_Click(object sender, RoutedEventArgs e) {
ApplicationCommands.Save.Execute(null, txtOben);
}
Hier wäre das Kommandoziel demnach eine Komponente namens txtOben.