Dela via


Översikt över kommandon

Commanding är en indatamekanism i Windows Presentation Foundation (WPF) som tillhandahåller indatahantering på en mer semantisk nivå än enhetens indata. Exempel på kommandon är Kopiera, Klipp utoch Klistra in åtgärder som finns i många program.

Den här översikten definierar vilka kommandon som finns i WPF, vilka klasser som ingår i kommandomodellen och hur du använder och skapar kommandon i dina program.

Det här avsnittet innehåller följande avsnitt:

Vad är kommandon

Kommandon har flera syften. Det första syftet är att separera semantiken och objektet som anropar ett kommando från logiken som kör kommandot. Detta gör det möjligt för flera och olika källor att anropa samma kommandologik, och det gör att kommandologiken kan anpassas för olika mål. Redigeringsåtgärderna Kopiera, Klipp utoch Klistra in, som finns i många program, kan till exempel anropas med hjälp av olika användaråtgärder om de implementeras med hjälp av kommandon. Ett program kan göra det möjligt för en användare att klippa ut markerade objekt eller text genom att antingen klicka på en knapp, välja ett objekt i en meny eller använda en nyckelkombination, till exempel CTRL+X. Genom att använda kommandon kan du binda varje typ av användaråtgärd till samma logik.

Ett annat syfte med kommandon är att ange om en åtgärd är tillgänglig. Om du vill fortsätta med att klippa ut ett objekt eller en text är åtgärden bara meningsfull när något har valts. Om en användare försöker klippa ut ett objekt eller text utan att ha något valt, skulle ingenting hända. För att indikera detta för användaren inaktiverar många program knappar och menyalternativ så att användaren vet om det är möjligt att utföra en åtgärd. Ett kommando kan ange om en åtgärd är möjlig genom att implementera metoden CanExecute. En knapp kan prenumerera på händelsen CanExecuteChanged och inaktiveras om CanExecute returnerar false eller aktiveras om CanExecute returnerar true.

Semantiken för ett kommando kan vara konsekvent mellan program och klasser, men logiken i åtgärden är specifik för det specifika objekt som hanteras. Tangentkombinationen CTRL+X anropar kommandot Klipp ut i textklasser, bildklasser och webbläsare, men den faktiska logiken för att utföra åtgärden Klipp ut definieras av programmet som utför klippningen. Med en RoutedCommand kan klienter implementera logiken. Ett textobjekt kan klippa ut den markerade texten i Urklipp, medan ett bildobjekt kan klippa ut den markerade bilden. När ett program hanterar den Executed händelsen har det åtkomst till kommandots mål och kan vidta lämpliga åtgärder beroende på målets typ.

Exempel på enkla kommandon i WPF

Det enklaste sättet att använda ett kommando i WPF är att använda en fördefinierad RoutedCommand från någon av kommandobiblioteksklasserna. använd en kontroll som har inbyggt stöd för att hantera kommandot; och använd en kontroll som har inbyggt stöd för att anropa ett kommando. Kommandot Paste är ett av de fördefinierade kommandona i klassen ApplicationCommands. Kontrollen TextBox har inbyggd logik för hantering av kommandot Paste. Och klassen MenuItem har inbyggt stöd för att anropa kommandon.

I följande exempel visas hur du konfigurerar en MenuItem så att när den klickas anropas kommandot Paste på en TextBox, förutsatt att TextBox har tangentbordsfokus.

<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

Fyra huvudbegrepp i WPF-kommandon

Den dirigerade kommandomodellen i WPF kan delas upp i fyra huvudbegrepp: kommandot, kommandokällan, kommandomålet och kommandobindningen:

  • Kommandot är den åtgärd som ska köras.

  • -kommandokällan är objektet som anropar kommandot.

  • kommandomål är det objekt som kommandot körs på.

  • -kommandobindningen är det objekt som mappar kommandologiken till kommandot.

I föregående exempel är kommandot Paste kommandot, MenuItem är kommandokällan, TextBox är kommandomålet och kommandobindningen tillhandahålls av TextBox kontroll. Det är värt att notera att det inte alltid är så att CommandBinding tillhandahålls av kontrollen som är kommandomålklassen. Ofta måste CommandBinding skapas av programutvecklaren, eller så kan CommandBinding kopplas till kommandomålets överordnade.

Kommandon

Kommandon i WPF skapas genom att implementera ICommand-gränssnittet. ICommand exponerar två metoder, Executeoch CanExecuteoch en händelse, CanExecuteChanged. Execute utför de åtgärder som är associerade med kommandot. CanExecute avgör om kommandot kan köras på det aktuella kommandomålet. CanExecuteChanged utlöses om kommandohanteraren som centraliserar kommandoåtgärderna identifierar en ändring i kommandokällan som kan ogiltigförklara ett kommando som har genererats men ännu inte körts av kommandobindningen. WPF-implementeringen av ICommand är klassen RoutedCommand och är i fokus för den här översikten.

De viktigaste indatakällorna i WPF är musen, tangentbordet, pennanteckningen och de dirigerade kommandona. Enhetsorienterade indata använder RoutedEvent för att meddela objekt på en programsida att en indatahändelse har inträffat. En RoutedCommand är inte annorlunda. Metoderna Execute och CanExecute för en RoutedCommand innehåller inte programlogiken för kommandot, utan snarare genererar dirigerade händelser som tunnlar och bubblar genom elementträdet tills de stöter på ett objekt med en CommandBinding. CommandBinding innehåller hanterare för dessa händelser och det är hanterare som utför kommandot. Mer information om händelseroutning i WPF finns i Översikt över dirigerade händelser.

Metoden Execute på en RoutedCommand genererar PreviewExecuted och Executed händelser på kommandomålet. Metoden CanExecute på en RoutedCommand genererar CanExecute och PreviewCanExecute händelser på kommandomålet. Dessa händelser tunnlar och sprider sig genom elementträdet tills de stöter på ett objekt som har en CommandBinding för det specifika kommandot.

WPF tillhandahåller en uppsättning vanliga dirigerade kommandon spridda över flera klasser: MediaCommands, ApplicationCommands, NavigationCommands, ComponentCommandsoch EditingCommands. Dessa klasser består endast av RoutedCommand objekt och inte implementeringslogiken för kommandot. Implementeringslogiken är ansvaret för det objekt som kommandot körs på.

Kommandokällor

En kommandokälla är det objekt som anropar kommandot. Exempel på kommandokällor är MenuItem, Buttonoch KeyGesture.

Kommandokällor i WPF implementerar vanligtvis ICommandSource-gränssnittet.

ICommandSource exponerar tre egenskaper: Command, CommandTargetoch CommandParameter:

WPF-klasserna som implementerar ICommandSource är ButtonBase, MenuItem, Hyperlinkoch InputBinding. ButtonBase, MenuItemoch Hyperlink anropar ett kommando när de klickas på, och en InputBinding anropar ett kommando när den InputGesture som är associerad med den utförs.

I följande exempel visas hur du använder en MenuItem i en ContextMenu som kommandokälla för kommandot Properties.

<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

Vanligtvis lyssnar en kommandokälla på händelsen CanExecuteChanged. Den här händelsen informerar kommandokällan om att kommandots möjlighet att köras på det aktuella kommandomålet kan ha ändrats. Kommandokällan kan fråga den aktuella statusen för RoutedCommand med hjälp av metoden CanExecute. Kommandokällan kan sedan inaktivera sig själv om kommandot inte kan köras. Ett exempel på detta är en MenuItem att nedtona sig själv när ett kommando inte kan köras.

En InputGesture kan användas som kommandokälla. Två typer av indatagester i WPF är KeyGesture och MouseGesture. Du kan se en KeyGesture som ett kortkommando, till exempel CTRL+C. En KeyGesture består av en Key och en uppsättning ModifierKeys. En MouseGesture består av en MouseAction och en valfri uppsättning ModifierKeys.

För att en InputGesture ska fungera som kommandokälla måste den associeras med ett kommando. Det finns några sätt att åstadkomma detta. Ett sätt är att använda en InputBinding.

I följande exempel visas hur du skapar en KeyBinding mellan en KeyGesture och en RoutedCommand.

<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)

Ett annat sätt att associera en InputGesture till en RoutedCommand är att lägga till InputGesture i InputGestureCollectionRoutedCommand.

I följande exempel visas hur du lägger till en KeyGesture i InputGestureCollection för en RoutedCommand.

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)

Kommandobindning

En CommandBinding associerar ett kommando med de händelsehanterare som implementerar kommandot.

Klassen CommandBinding innehåller en Command-egenskap samt PreviewExecuted-, Executed-, PreviewCanExecute- och CanExecute-händelser.

Command är det kommando som CommandBinding associeras med. Händelsehanterarna som är kopplade till PreviewExecuted- och Executed-händelserna implementerar kommandologiken. Händelsehanterarna som är kopplade till PreviewCanExecute- och CanExecute-händelserna avgör om kommandot kan köras på det aktuella kommandomålet.

I följande exempel visas hur du skapar en CommandBinding på roten Window för ett program. CommandBinding associerar kommandot Open med Executed- och CanExecute-hanterare.

<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)

Därefter skapas ExecutedRoutedEventHandler och en CanExecuteRoutedEventHandler. ExecutedRoutedEventHandler öppnar en MessageBox som visar en sträng som säger att kommandot har körts. CanExecuteRoutedEventHandler sätter egenskapen CanExecute till true.

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

En CommandBinding är kopplad till ett specifikt objekt, till exempel programmets rot Window eller en kontroll. Det objekt som CommandBinding är kopplat till definierar bindningens omfång. Till exempel kan en CommandBinding som är kopplad till en överordnad till kommandomålet nås av händelsen Executed, men en CommandBinding som är kopplad till en underordnad till kommandomålet kan inte nås. Detta är en direkt följd av hur en RoutedEvent tunnlar och bubblar från objektet som orsakar händelsen.

I vissa situationer är CommandBinding kopplat till själva kommandomålet, till exempel med klassen TextBox och kommandona Cut, Copyoch Paste. Ganska ofta är det dock enklare att koppla CommandBinding till en överordnad till kommandomålet, till exempel huvud-Window eller programobjektet, särskilt om samma CommandBinding kan användas för flera kommandomål. Det här är designbeslut som du bör tänka på när du skapar din kommandoinfrastruktur.

Kommandomål

Kommandomålet är det element som kommandot körs på. När det gäller en RoutedCommandär kommandomålet det element där routningen av Executed och CanExecute startar. Som nämnts tidigare i WPF, gäller egenskapen CommandTargetICommandSource endast när ICommand är en RoutedCommand. Om CommandTarget anges på en ICommandSource och motsvarande kommando inte är en RoutedCommandignoreras kommandomålet.

Kommandokällan kan uttryckligen ange kommandomålet. Om kommandomålet inte har definierats används elementet med tangentbordsfokus som kommandomål. En av fördelarna med att använda elementet med tangentbordsfokus som kommandomål är att programutvecklaren kan använda samma kommandokälla för att anropa ett kommando på flera mål utan att behöva hålla reda på kommandomålet. Om en MenuItem till exempel anropar kommandot Klistra in i ett program som har en TextBox kontroll och en PasswordBox kontroll, kan målet antingen vara TextBox eller PasswordBox beroende på vilken kontroll som har tangentbordsfokus.

I följande exempel visas hur du uttryckligen anger kommandomålet i markering och i koden bakom.

<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

The CommandManager

CommandManager hanterar ett antal kommandorelaterade funktioner. Den innehåller en uppsättning statiska metoder för att lägga till och ta bort PreviewExecuted, Executed, PreviewCanExecuteoch CanExecute händelsehanterare till och från ett visst element. Det ger ett sätt att registrera CommandBinding och InputBinding objekt på en specifik klass. CommandManager tillhandahåller också ett sätt att via händelsen RequerySuggested meddela ett kommando när det ska generera CanExecuteChanged händelsen.

Metoden InvalidateRequerySuggested tvingar CommandManager att generera händelsen RequerySuggested. Detta är användbart för villkor som ska inaktivera/aktivera ett kommando, men som inte är villkor som CommandManager känner till.

Kommandobibliotek

WPF innehåller en uppsättning fördefinierade kommandon. Kommandobiblioteket består av följande klasser: ApplicationCommands, NavigationCommands, MediaCommands, EditingCommandsoch ComponentCommands. Dessa klasser innehåller kommandon som Cut, BrowseBack och BrowseForward, Play, Stopoch Pause.

Många av dessa kommandon innehåller en uppsättning standardbindningar för indata. Om du till exempel anger att programmet hanterar kopieringskommandot får du automatiskt tangentbordsbindningen "CTRL+C" Du får även bindningar för andra indataenheter, till exempel Penngester i Tablet PC och talinformation.

När du refererar till kommandon i de olika kommandobiblioteken med XAML kan du vanligtvis utelämna klassnamnet för den biblioteksklass som exponerar egenskapen för statiska kommandon. I allmänhet är kommandonamnen otvetydiga som strängar, och de tillhörande typerna finns för att tillhandahålla en logisk gruppering av kommandon men är inte nödvändiga för att lösa tvetydigheter. Du kan till exempel ange Command="Cut" i stället för de mer utförliga Command="ApplicationCommands.Cut". Det här är en bekvämlighetsmekanism som är inbyggd i WPF XAML-processorn för kommandon (mer exakt är det ett typkonverterarbeteende för ICommand, som WPF XAML-processorn refererar till vid belastning).

Skapa anpassade kommandon

Om kommandona i kommandobiblioteksklasserna inte uppfyller dina behov kan du skapa egna kommandon. Det finns två sätt att skapa ett anpassat kommando. Den första är att börja från grunden och implementera ICommand-gränssnittet. Det andra sättet, och den vanligaste metoden, är att skapa en RoutedCommand eller en RoutedUICommand.

Ett exempel på hur du skapar en anpassad RoutedCommandfinns i Skapa ett anpassat RoutedCommand-exempel.

Se även