Befehlsübersicht

Befehle sind ein Eingabemechanismus in Windows Presentation Foundation (WPF), der die Verarbeitung von Eingaben auf einer semantischeren Ebene als die Geräteeingabe ermöglicht. Beispiele für Befehle sind die in vielen Programmen enthaltenen Vorgänge Kopieren, Ausschneiden und Einfügen.

In dieser Übersicht wird definiert, was Befehle in WPF sind, welche Klassen Teil des Befehlsmodells sind und wie Sie Befehle in Ihren Anwendungen verwenden und erstellen.

Dieses Thema enthält folgende Abschnitte:

Was sind Befehle?

Befehle haben mehrere Zwecke. Der erste ist die Trennung der Semantik und des von einem Befehl aufgerufenen Objekts von der Logik, die den Befehl ausführt. Dadurch können mehrere und unterschiedliche Quellen dieselbe Befehlslogik aufrufen, und die Befehlslogik kann für verschiedene Ziele angepasst werden. Die Bearbeitungsvorgänge Kopieren, Ausschneiden und Einfügen, die in vielen Programmen vorkommen, können z.B. mithilfe anderer Benutzeraktionen aufgerufen werden, wenn sie mithilfe von Befehlen implementiert werden. In manchen Anwendungen lassen sich ausgewählte Objekte oder Text durch einen Klick auf eine Schaltfläche, durch Auswählen eines Elements in einem Menü oder über eine Tastenkombination wie STRG+X ausschneiden. Mithilfe von Befehlen können Sie jede Art von Benutzeraktion an die gleiche Logik binden.

Ein weiterer Zweck von Befehlen ist es, die Verfügbarkeit von Aktionen anzugeben. Setzen wir das vorherige Beispiel fort: Das Ausschneiden eines Objekts oder Texts ist nur sinnvoll, wenn ein Objekt ausgewählt wurde. Wenn ein Benutzer versucht, ein Objekt oder Text auszuschneiden, ohne dass etwas ausgewählt wurde, passiert nichts. Um dies für Benutzer kenntlich zu machen, werden Schaltflächen und Menüelemente in vielen Anwendungen deaktiviert. So weiß der Benutzer, ob es möglich ist, eine Aktion auszuführen. Ein Befehl kann durch Implementierung der Methode CanExecute angegeben, ob eine Aktion möglich ist. Eine Schaltfläche kann das Ereignis CanExecuteChanged abonnieren und deaktiviert werden, wenn CanExecute den Wert false zurückgibt, oder aktiviert werden, wenn CanExecute den Wert true zurückgibt.

Die Semantik eines Befehls kann anwendungs- und klassenübergreifend konsistent sein. Die Logik einer Aktion ist aber dem betreffenden Objekt eigen, auf das die Aktion ausgeführt wird. Die Tastenkombination STRG+X ruft zwar den Befehl Ausschneiden in Textklassen, Bildklassen und Webbrowsern auf, aber die eigentliche Logik für die Ausführung des Vorgangs Ausschneiden wird von der Anwendung definiert, die ihn ausführt. RoutedCommand aktiviert Clients, um die Logik zu implementieren. Ein Textobjekt kann den markierten Text ausschneiden und in die Zwischenablage kopieren, während ein Bildobjekt das markierte Bild ausschneiden kann. Wenn eine Anwendung das Ereignis Executed verarbeitet, kann sie auf das Ziel des Befehls zugreifen und je nach Zielart geeignete Maßnahmen ergreifen.

Beispiel eines einfachen Befehls in WPF

Die einfachste Möglichkeit, einen Befehl in WPF zu verwenden, ist die Verwendung eines vordefinierten RoutedCommand aus einer der Klassen der Befehlsbibliothek, die Verwendung eines Steuerelements mit nativer Unterstützung für die Verarbeitung des Befehls und die Verwendung eines Steuerelements mit nativer Unterstützung zum Aufrufen eines Befehls. Der Befehl Paste ist einer der vordefinierten Befehle in der Klasse ApplicationCommands. Das Steuerelement TextBox verfügt über integrierte Logik zum Verarbeiten des Befehls Paste. Und die Klasse MenuItem verfügt über native Unterstützung für das Aufrufen von Befehlen.

Im folgenden Beispiel wird dargestellt, wie MenuItem eingerichtet wird, damit der Befehl Paste auf TextBox mit der Annahme, dass TextBox über den Tastaturfokus verfügt, aufgerufen wird, wenn darauf geklickt wird.

<StackPanel>
  <Menu>
    <MenuItem Command="ApplicationCommands.Paste" />
  </Menu>
  <TextBox />
</StackPanel>
// Creating the UI objects
StackPanel mainStackPanel = new StackPanel();
TextBox pasteTextBox = new TextBox();
Menu stackPanelMenu = new Menu();
MenuItem pasteMenuItem = new MenuItem();

// Adding objects to the panel and the menu
stackPanelMenu.Items.Add(pasteMenuItem);
mainStackPanel.Children.Add(stackPanelMenu);
mainStackPanel.Children.Add(pasteTextBox);

// Setting the command to the Paste command
pasteMenuItem.Command = ApplicationCommands.Paste;

// Setting the command target to the TextBox
pasteMenuItem.CommandTarget = pasteTextBox;
' Creating the UI objects
Dim mainStackPanel As New StackPanel()
Dim pasteTextBox As New TextBox()
Dim stackPanelMenu As New Menu()
Dim pasteMenuItem As New MenuItem()

' Adding objects to the panel and the menu
stackPanelMenu.Items.Add(pasteMenuItem)
mainStackPanel.Children.Add(stackPanelMenu)
mainStackPanel.Children.Add(pasteTextBox)

' Setting the command to the Paste command
pasteMenuItem.Command = ApplicationCommands.Paste

Vier Hauptkonzepte für WPF-Befehle

Das Modell der weitergeleiteten Befehle in WPF kann in vier Hauptkonzepte unterteilt werden: den Befehl, die Befehlsquelle, das Befehlsziel und die Befehlsbindung:

  • Die Befehl ist die Aktion, die ausgeführt werden soll.

  • Die Befehlsquelle ist das Objekt, das den Befehl aufruft.

  • Das Befehlsziel ist das Objekt, auf das der Befehl ausgeführt wird.

  • Die Befehlsbindung ist das Objekt, das den Befehl der Befehlslogik zuordnet.

Im vorherigen Beispiel ist Paste der Befehl, MenuItem die Befehlsquelle, TextBox das Befehlsziel und die Befehlsbindung wird vom Steuerelement TextBox bereitgestellt. Beachten Sie, dass CommandBinding nicht immer vom Steuerelement angegeben wird, das die Befehlszielklasse darstellt. CommandBinding muss oft vom Anwendungsentwickler erstellt werden, sonst kann es vorkommen, dass CommandBinding mit einem Vorgänger des Befehlsziels verbunden ist.

Befehle

Befehle in WPF werden durch die Implementierung der Schnittstelle ICommand erstellt. ICommand stellt die zwei Methoden Execute und CanExecute sowie ein CanExecuteChanged-Ereignis zur Verfügung. Execute führt die Aktionen durch, die dem Befehl zugeordnet sind. CanExecute bestimmt, ob der Befehl auf dem aktuellen Befehlsziel ausgeführt werden kann. CanExecuteChanged wird ausgelöst, wenn der Befehls-Manager, der die Befehlsvorgänge zentralisiert, eine Änderung in der Befehlsquelle erkennt, die einen durch die Befehlsbindung ausgelösten Befehl, der noch nicht ausgeführt wurde, für ungültig erklären kann. Die WPF-Implementierung von ICommand ist die Klasse RoutedCommand und steht im Mittelpunkt dieser Übersicht.

Die Hauptquellen für Eingabe in WPF sind Maus, Tastatur, Freihandeingaben und weitergeleitete Befehle. Stärker geräteorientierte Eingaben verwenden ein RoutedEvent-Objekt, um Objekte auf einer Anwendungsseite darüber zu benachrichtigen, das ein Eingabeereignis aufgetreten ist. Für RoutedCommand besteht kein Unterschied. Die Methoden Execute und CanExecute von RoutedCommand enthalten keine Anwendungslogik für den Befehl, stattdessen lösen sie Routingereignisse aus, die durch die Elementstruktur tunneln und bubblen, bis sie ein Objekt mit CommandBinding finden. CommandBinding enthält die Handler für diese Ereignisse, und die Handler führen den Befehl aus. Weitere Informationen zum Ereignisrouting in WPF finden Sie unter Übersicht über Routingereignisse.

Die Methode Execute auf RoutedCommand löst die Ereignisse PreviewExecuted und Executed auf dem Befehlsziel aus. Die Methode CanExecute auf RoutedCommand löst die Ereignisse CanExecute und PreviewCanExecute auf dem Befehlsziel aus. Diese Ereignisse tunneln und bubblen in der Elementstruktur, bis sie ein Objekt finden, das CommandBinding für diesen speziellen Befehl ausweist.

WPF stellt eine Reihe gängiger weitergeleiteter Befehle bereit, die auf mehrere Klassen verteilt sind: MediaCommands, ApplicationCommands, NavigationCommands, ComponentCommands und EditingCommands. Diese Klassen umfassen nur RoutedCommand-Objekte und nicht die Implementierungslogik des Befehls. Die Implementierungslogik ist die Verantwortung des Objekts, für das der Befehl ausgeführt wird.

Befehlsquellen

Eine Befehlsquelle ist das Objekt, das den Befehl aufruft. MenuItem, Button und KeyGesture sind Beispiele für Befehlsquellen.

Befehlsquellen in WPF implementieren im Allgemeinen die Schnittstelle ICommandSource.

ICommandSource stellt drei Eigenschaften zur Verfügung: Command, CommandTarget und CommandParameter:

Die WPF-Klassen, die ICommandSource implementieren, sind ButtonBase, MenuItem, Hyperlink und InputBinding. ButtonBase, MenuItem und Hyperlink rufen einen Befehl auf, wenn auf sie geklickt wird, und InputBinding ruft einen Befehl auf, wenn das zugeordnete InputGesture ausgeführt wird.

Im folgenden Beispiel wird veranschaulicht, wie MenuItem in ContextMenu als Befehlsquelle für den Befehl Properties verwendet wird.

<StackPanel>
  <StackPanel.ContextMenu>
    <ContextMenu>
      <MenuItem Command="ApplicationCommands.Properties" />
    </ContextMenu>
  </StackPanel.ContextMenu>
</StackPanel>
StackPanel cmdSourcePanel = new StackPanel();
ContextMenu cmdSourceContextMenu = new ContextMenu();
MenuItem cmdSourceMenuItem = new MenuItem();

// Add ContextMenu to the StackPanel.
cmdSourcePanel.ContextMenu = cmdSourceContextMenu;
cmdSourcePanel.ContextMenu.Items.Add(cmdSourceMenuItem);

// Associate Command with MenuItem.
cmdSourceMenuItem.Command = ApplicationCommands.Properties;
Dim cmdSourcePanel As New StackPanel()
Dim cmdSourceContextMenu As New ContextMenu()
Dim cmdSourceMenuItem As New MenuItem()

' Add ContextMenu to the StackPanel.
cmdSourcePanel.ContextMenu = cmdSourceContextMenu
cmdSourcePanel.ContextMenu.Items.Add(cmdSourceMenuItem)

' Associate Command with MenuItem.
cmdSourceMenuItem.Command = ApplicationCommands.Properties

In der Regel überwacht eine Befehlsquelle das Ereignis CanExecuteChanged. Dieses Ereignis teilt der Befehlsquelle mit, dass sich die Fähigkeit des Befehls zur Ausführung für das aktuelle Befehlsziel möglicherweise geändert hat. Die Befehlsquelle kann mithilfe der Methode CanExecute den aktuellen Status von RoutedCommand abfragen. Befehlsquelle sich kann dann selbst deaktivieren, wenn der Befehl nicht ausgeführt werden kann. Ein Beispiel hierfür ist ein ausgegrautes MenuItem, wenn ein Befehl nicht ausgeführt werden kann.

InputGesture kann als Befehlsquelle verwendet werden. KeyGesture und MouseGesture sind zwei Arten von Eingabegesten in WPF. Sie können sich KeyGesture wie eine Tastenkombination vorstellen, z.B. STRG+C. KeyGesture besteht aus einem Key und einer Reihe von ModifierKeys. MouseGesture besteht aus einem MouseAction und einer optionalen Reihe von ModifierKeys.

Damit InputGesture sich wie eine Befehlsquelle verhält, muss es einem Befehl zugeordnet werden. Hierzu gibt es zwei Möglichkeiten. Eine Möglichkeit ist die Verwendung von InputBinding.

Im folgenden Beispiel wird veranschaulicht, wie ein KeyBinding zwischen KeyGesture und RoutedCommand erstellt wird.

<Window.InputBindings>
  <KeyBinding Key="B"
              Modifiers="Control" 
              Command="ApplicationCommands.Open" />
</Window.InputBindings>
KeyGesture OpenKeyGesture = new KeyGesture(
    Key.B,
    ModifierKeys.Control);

KeyBinding OpenCmdKeybinding = new KeyBinding(
    ApplicationCommands.Open,
    OpenKeyGesture);

this.InputBindings.Add(OpenCmdKeybinding);
Dim OpenKeyGesture As New KeyGesture(Key.B, ModifierKeys.Control)

Dim OpenCmdKeybinding As New KeyBinding(ApplicationCommands.Open, OpenKeyGesture)

Me.InputBindings.Add(OpenCmdKeybinding)

Eine weitere Möglichkeit, um InputGesture einem RoutedCommand zuzuordnen, besteht darin, InputGesture dem InputGestureCollection auf RoutedCommand hinzuzufügen.

Im folgenden Beispiel wird gezeigt, wie ein KeyGesture der InputGestureCollection von RoutedCommand hinzugefügt wird.

KeyGesture OpenCmdKeyGesture = new KeyGesture(
    Key.B,
    ModifierKeys.Control);

ApplicationCommands.Open.InputGestures.Add(OpenCmdKeyGesture);
Dim OpenCmdKeyGesture As New KeyGesture(Key.B, ModifierKeys.Control)

ApplicationCommands.Open.InputGestures.Add(OpenCmdKeyGesture)

CommandBinding

CommandBinding ordnet ein Befehl den Ereignishandlern zu, die den Befehl implementieren.

Die Klasse CommandBinding enthält eine Command-Eigenschaft und die Ereignisse PreviewExecuted, Executed, PreviewCanExecute und CanExecute.

Command ist der Befehl, dem CommandBinding zugeordnet wird. Die Ereignishandler, die den Ereignissen PreviewExecuted und Executed zugeordnet sind, implementieren die Befehlslogik. Die Ereignishandler, die den Ereignissen PreviewCanExecute und CanExecute zugeordnet sind, bestimmen, ob der Befehl auf dem aktuellen Befehlsziel ausgeführt werden kann.

Das folgende Beispiel veranschaulicht das Erstellen von CommandBinding auf dem Window-Stamm einer Anwendung. CommandBinding ordnet den Befehl Open den Handlern Executed und CanExecute zu.

<Window.CommandBindings>
  <CommandBinding Command="ApplicationCommands.Open"
                  Executed="OpenCmdExecuted"
                  CanExecute="OpenCmdCanExecute"/>
</Window.CommandBindings>
// Creating CommandBinding and attaching an Executed and CanExecute handler
CommandBinding OpenCmdBinding = new CommandBinding(
    ApplicationCommands.Open,
    OpenCmdExecuted,
    OpenCmdCanExecute);

this.CommandBindings.Add(OpenCmdBinding);
' Creating CommandBinding and attaching an Executed and CanExecute handler
Dim OpenCmdBinding As New CommandBinding(ApplicationCommands.Open, AddressOf OpenCmdExecuted, AddressOf OpenCmdCanExecute)

Me.CommandBindings.Add(OpenCmdBinding)

Als Nächstes werden die ExecutedRoutedEventHandler- und CanExecuteRoutedEventHandler-Objekte erstellt. ExecutedRoutedEventHandler öffnet eine MessageBox-Klasse, die eine Zeichenfolge anzeigt, die angibt, dass der Befehl ausgeführt wurde. Der CanExecuteRoutedEventHandler legt die CanExecute-Eigenschaft auf true fest.

void OpenCmdExecuted(object target, ExecutedRoutedEventArgs e)
{
    String command, targetobj;
    command = ((RoutedCommand)e.Command).Name;
    targetobj = ((FrameworkElement)target).Name;
    MessageBox.Show("The " + command +  " command has been invoked on target object " + targetobj);
}
Private Sub OpenCmdExecuted(ByVal sender As Object, ByVal e As ExecutedRoutedEventArgs)
    Dim command, targetobj As String
    command = CType(e.Command, RoutedCommand).Name
    targetobj = CType(sender, FrameworkElement).Name
    MessageBox.Show("The " + command + " command has been invoked on target object " + targetobj)
End Sub
void OpenCmdCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
    e.CanExecute = true;
}
Private Sub OpenCmdCanExecute(ByVal sender As Object, ByVal e As CanExecuteRoutedEventArgs)
    e.CanExecute = True
End Sub

CommandBinding wird einem spezifischen Objekt zugeordnet, z.B. dem Window-Stamm der Anwendung oder eines Steuerelements. Das Objekt, dem CommandBinding zugeordnet ist, definiert den Bereich der Bindung. Einem Vorgänger des Befehlsziels zugeordnete CommandBinding kann beispielsweise mit dem Ereignis Executed erreicht werden, jedoch kann eine CommandBinding, die einem Nachfolger des Befehlsziels zugeordnet ist, nicht erreicht werden. Dies ist eine direkte Folge der Art und Weise, wie RoutedEvent vom Objekt in der Hierarchie ab- und aufsteigt (tunneln und bubbeln), das das Ereignis auslöst.

In manchen Situationen wird CommandBinding dem Befehlsziel selbst zugeordnet, z.B. bei der Klasse TextBox und den Befehlen Cut, Copy und Paste. In vielen Fällen ist er einfacher, CommandBinding dem Vorgänger des Befehlsziels zuzuordnen, z.B. der Window-Hauptklasse oder dem Anwendungsobjekt, insbesondere wenn dieselbe CommandBinding für mehrere Befehlsziele verwendet werden kann. Bei Erstellung Ihrer Befehlsinfrastruktur sollten Sie einige Entwurfsentscheidungen in beachten.

Befehlsziel

Das Befehlsziel ist das Element, für das der Befehl ausgeführt wird. Bei RoutedCommand ist das Befehlsziel das Element, bei dem das Routing von Executed und CanExecute startet. Wie bereits erwähnt, ist die CommandTarget-Eigenschaft für ICommandSource in WPF nur anwendbar, wenn ICommand ein RoutedCommand ist. Wenn CommandTarget auf eine ICommandSource festgelegt und der entsprechende Befehl kein RoutedCommand ist, wird das Befehlsziel ignoriert.

Die Befehlsquelle kann das Befehlsziel explizit festlegen. Wenn das Befehlsziel nicht definiert ist, wird das Element mit dem Tastaturfokus als Befehlsziel verwendet. Einer der Vorteile daran, das Elements mit dem Tastaturfokus als Befehlsziel zu verwenden, ist die Tatsache, dass dies dem Anwendungsentwickler erlaubt, dieselbe Befehlsquelle zu verwenden, um einen Befehl für mehrere Ziele aufzurufen, ohne das Befehlsziel nachverfolgen zu müssen. Wenn MenuItem den Befehl Paste in einer Anwendung aufruft, die über ein TextBox- und ein PasswordBox-Steuerelement verfügt, kann das Ziel entweder TextBox oder PasswordBox sein, je nachdem, welches Steuerelement über den Tastaturfokus verfügt.

Im folgenden Beispiel wird das explizite Festlegen des Befehlsziels in Markup und der CodeBehind-Datei veranschaulicht.

<StackPanel>
  <Menu>
    <MenuItem Command="ApplicationCommands.Paste"
              CommandTarget="{Binding ElementName=mainTextBox}" />
  </Menu>
  <TextBox Name="mainTextBox"/>
</StackPanel>
// Creating the UI objects
StackPanel mainStackPanel = new StackPanel();
TextBox pasteTextBox = new TextBox();
Menu stackPanelMenu = new Menu();
MenuItem pasteMenuItem = new MenuItem();

// Adding objects to the panel and the menu
stackPanelMenu.Items.Add(pasteMenuItem);
mainStackPanel.Children.Add(stackPanelMenu);
mainStackPanel.Children.Add(pasteTextBox);

// Setting the command to the Paste command
pasteMenuItem.Command = ApplicationCommands.Paste;

// Setting the command target to the TextBox
pasteMenuItem.CommandTarget = pasteTextBox;
' Creating the UI objects
Dim mainStackPanel As New StackPanel()
Dim pasteTextBox As New TextBox()
Dim stackPanelMenu As New Menu()
Dim pasteMenuItem As New MenuItem()

' Adding objects to the panel and the menu
stackPanelMenu.Items.Add(pasteMenuItem)
mainStackPanel.Children.Add(stackPanelMenu)
mainStackPanel.Children.Add(pasteTextBox)

' Setting the command to the Paste command
pasteMenuItem.Command = ApplicationCommands.Paste

Der CommandManager

CommandManager stellt eine Reihe von befehlsbezogenen Funktionen bereit. Er bietet eine Reihe von statischen Methoden zum Hinzufügen und Entfernen der Ereignishandler PreviewExecuted, Executed, PreviewCanExecute und CanExecute in und aus einem spezifischen Element. Er ermöglicht das Registrieren der Objekte CommandBinding und InputBinding auf einer bestimmten Klasse. CommandManager ermöglicht durch das Ereignis RequerySuggested außerdem das Benachrichtigen eines Befehls, wenn er das Ereignis CanExecuteChanged auslösen soll.

Die Methode InvalidateRequerySuggested zwingt CommandManager zum Auslösen des Ereignisses RequerySuggested. Dies ist für Bedingungen nützlich, die einen Befehl nicht deaktivieren oder aktivieren sollen, aber keine Bedingungen sind, die CommandManager beachtet.

Befehlsbibliothek

WPF stellt eine Reihe von vordefinierten Befehlen bereit. Die Befehlsbibliothek besteht aus den folgenden Klassen: ApplicationCommands, NavigationCommands, MediaCommands, EditingCommands und ComponentCommands. Diese Klassen stellen Befehle wie die Folgenden bereit: Cut, BrowseBack, BrowseForward, Play, Stop und Pause.

Viele dieser Befehle enthalten einen Satz von Standardeingabebindungen. Wenn Sie beispielsweise angeben, dass Ihre Anwendung den Kopierbefehl verarbeitet, erhalten Sie automatisch die Tastaturbindung STRG+C. Sie erhalten zudem Bindungen für andere Eingabegeräte, beispielsweise Stiftgesten für Tablet-PCs und Sprachinformationen.

Wenn Sie in den verschiedenen Bibliotheken mit XAML auf Befehle verweisen, können Sie normalerweise den Klassennamen der Bibliotheksklasse weglassen, die die statische Befehlseigenschaft verfügbar macht. In der Regel sind die Befehlsnamen als Zeichenfolgen eindeutig, und die besitzenden Typen sind vorhanden, um eine logische Gruppierung der Befehle bereitzustellen. Zur Mehrdeutigkeitsvermeidung sind sie nicht erforderlich. Sie können z.B. Command="Cut" anstelle des ausführlicheren Command="ApplicationCommands.Cut" angeben. Dies ist ein benutzerfreundlicher Mechanismus, der in den WPF-XAML-Prozessor für Befehle integriertet ist. (Genauer gesagt ist dies das Verhalten des Typkonverters ICommand, auf den der WPF-XAML-Prozessor zur Ladezeit verweist.)

Erstellen von benutzerdefinierten Befehlen

Wenn die Befehle in den Klassen der Befehlsbibliothek nicht Ihren Bedürfnissen entsprechen, können Sie eigene Befehle erstellen. Sie können auf zwei Arten und Weisen einen benutzerdefinierten Befehl erstellen. Die Erste besteht darin, von Grund auf zu beginnen und die Schnittstelle ICommand zu implementieren. Die andere, gängigere Möglichkeit besteht in der Erstellung eines RoutedCommand oder RoutedUICommand.

Ein Beispiel für das Erstellen eines benutzerdefinierten RoutedCommand finden Sie unter Create a Custom RoutedCommand Sample (Erstellen eines benutzerdefinierten „RoutedCommand“-Beispiels).

Weitere Informationen