Condividi tramite


Panoramica dei comandi

Commanding è un meccanismo di input in Windows Presentation Foundation (WPF) che fornisce la gestione dell'input a un livello più semantico rispetto a quello del dispositivo. Esempi di comandi sono le operazioni Copia, Tagliae Incolla trovate in molte applicazioni.

Questa panoramica definisce quali comandi si trovano in WPF, quali classi fanno parte del modello di comando e come usare e creare comandi nelle applicazioni.

Questo argomento contiene le sezioni seguenti:

Che cosa sono i comandi

I comandi hanno diversi scopi. Il primo scopo è separare la semantica e l'oggetto che richiama un comando dalla logica che esegue il comando. Ciò consente a più origini diverse di richiamare la stessa logica di comando e di personalizzare la logica dei comandi per destinazioni diverse. Ad esempio, le operazioni di modifica Copia, Tagliae Incolla, disponibili in molte applicazioni, possono essere richiamate usando azioni utente diverse se implementate tramite comandi. Un'applicazione può consentire a un utente di tagliare oggetti o testo selezionati facendo clic su un pulsante, scegliendo un elemento in un menu o usando una combinazione di tasti, ad esempio CTRL+X. Usando i comandi, è possibile associare ogni tipo di azione dell'utente alla stessa logica.

Un altro scopo dei comandi è indicare se è disponibile un'azione. Per continuare l'esempio di taglio di un oggetto o di un testo, l'azione ha senso solo quando viene selezionato un elemento. Se un utente tenta di tagliare un oggetto o un testo senza alcuna selezione, non accadrebbe nulla. Per indicare questo valore all'utente, molte applicazioni disabilitano pulsanti e voci di menu in modo che l'utente sappia se è possibile eseguire un'azione. Un comando può indicare se un'azione è possibile implementando il metodo CanExecute. Un pulsante può sottoscrivere l'evento CanExecuteChanged ed essere disabilitato se CanExecute restituisce false o essere abilitato se CanExecute restituisce true.

La semantica di un comando può essere coerente tra applicazioni e classi, ma la logica dell'azione è specifica per l'oggetto specifico su cui agisce. La combinazione di tasti CTRL+X richiama il comando Cut nelle classi di testo, nelle classi di immagini e nei browser Web, ma la logica effettiva per eseguire l'operazione Cut viene definita dall'applicazione che esegue il taglio. Un RoutedCommand consente ai client di implementare la logica. Un oggetto di testo può tagliare il testo selezionato negli Appunti, mentre un oggetto immagine può tagliare l'immagine selezionata. Quando un'applicazione gestisce l'evento Executed, ha accesso alla destinazione del comando e può eseguire azioni appropriate a seconda del tipo di destinazione.

Esempio di comando semplice in WPF

Il modo più semplice per usare un comando in WPF consiste nell'usare un RoutedCommand predefinito da una delle classi della libreria dei comandi; usare un controllo con supporto nativo per la gestione del comando; e usare un controllo con supporto nativo per richiamare un comando. Il comando Paste è uno dei comandi predefiniti nella classe ApplicationCommands. Il controllo TextBox ha una logica incorporata per la gestione del comando Paste. E la classe MenuItem ha il supporto nativo per richiamare i comandi.

Nell'esempio seguente viene illustrato come configurare un MenuItem in modo che, quando viene fatto clic, richiami il comando Paste su un TextBox, a condizione che l'TextBox abbia lo stato attivo della tastiera.

<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

Quattro concetti principali del comando WPF

Il modello di comando indirizzato in WPF può essere suddiviso in quattro concetti principali: il comando, l'origine del comando, la destinazione del comando e l'associazione di comandi:

  • Il comando è l'azione da eseguire.

  • L'origine del comando è l'oggetto che richiama il comando.

  • La destinazione del comando è l'oggetto in cui viene eseguito il comando.

  • L'associazione del comando al legame è l'oggetto che mappa la logica del comando al comando.

Nell'esempio precedente, il comando Paste è il comando , il MenuItem è l'origine del comando, il TextBox è la destinazione del comando e l'associazione di comandi viene fornita dal controllo TextBox. Vale la pena notare che non è sempre vero che il CommandBinding venga fornito dal controllo che costituisce la classe di destinazione del comando. Spesso il CommandBinding deve essere creato dallo sviluppatore dell'applicazione o il CommandBinding potrebbe essere collegato a un predecessore della destinazione del comando.

Comandi

I comandi in WPF vengono creati implementando l'interfaccia ICommand. ICommand espone due metodi, Executee CanExecute, e un evento, CanExecuteChanged. Execute esegue le azioni associate al comando . CanExecute determina se il comando può essere eseguito sulla destinazione corrente del comando. CanExecuteChanged viene generato se il gestore comandi che centralizza le operazioni di comando rileva una modifica nell'origine del comando che potrebbe invalidare un comando generato ma non ancora eseguito dall'associazione di comandi. L'implementazione WPF di ICommand è la classe RoutedCommand ed è l'obiettivo di questa panoramica.

Le fonti principali di input in WPF sono il mouse, la tastiera, l'input stilo e i comandi instradati. Gli input più orientati ai dispositivi usano il codice RoutedEvent per notificare agli oggetti in una pagina dell'applicazione che si è verificato un evento di input. Un RoutedCommand non è diverso. I metodi Execute e CanExecute di un RoutedCommand non contengono la logica dell'applicazione per il comando, ma sollevano eventi instradati che tunnelizzano e propagano attraverso l'albero degli elementi fino a quando non incontrano un oggetto con un CommandBinding. Il CommandBinding contiene i gestori per questi eventi ed è il gestore che esegue il comando. Per ulteriori informazioni sugli eventi a percorso in WPF, vedere Panoramica degli eventi a percorso.

Il metodo Execute su un RoutedCommand genera gli eventi PreviewExecuted e Executed sul target del comando. Il metodo CanExecute su un RoutedCommand genera gli eventi CanExecute e PreviewCanExecute sul target del comando. Questi eventi attraversano e si propagano nell'albero degli elementi fino a quando non incontrano un oggetto con un CommandBinding per quel particolare comando.

WPF fornisce un set di comandi indirizzati comuni distribuiti tra diverse classi: MediaCommands, ApplicationCommands, NavigationCommands, ComponentCommandse EditingCommands. Queste classi sono costituite solo dagli oggetti RoutedCommand e non dalla logica di implementazione del comando. La logica di implementazione è responsabilità dell'oggetto su cui viene eseguito il comando.

Origini dei comandi

Un'origine del comando è l'oggetto che richiama il comando. Esempi di origini dei comandi sono MenuItem, Buttone KeyGesture.

Le fonti dei comandi in WPF in genere implementano l'interfaccia ICommandSource.

ICommandSource espone tre proprietà: Command, CommandTargete CommandParameter:

Le classi WPF che implementano ICommandSource sono ButtonBase, MenuItem, Hyperlinke InputBinding. ButtonBase, MenuIteme Hyperlink richiamano un comando quando vengono cliccati, e un InputBinding richiama un comando quando viene eseguito il InputGesture associato.

Nell'esempio seguente viene illustrato come usare un MenuItem in un ContextMenu come origine dei comandi per il comando 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

In genere, un'origine del comando ascolterà l'evento CanExecuteChanged. Questo evento informa la sorgente del comando che la capacità di eseguire il comando sulla destinazione del comando corrente potrebbe essere cambiata. La sorgente del comando può interrogare lo stato corrente del RoutedCommand usando il metodo CanExecute. L'origine del comando può quindi disabilitarsi se il comando non può essere eseguito. Un esempio di ciò è quando un MenuItem si sbiadisce automaticamente perché un comando non può essere eseguito.

Un InputGesture può essere usato come sorgente di comando. Due tipi di gesti di input in WPF sono i KeyGesture e i MouseGesture. Puoi pensare a un KeyGesture come a un tasto di scelta rapida, ad esempio CTRL+C. Un KeyGesture è costituito da un Key e da un set di ModifierKeys. Un MouseGesture è costituito da un MouseAction e da un set facoltativo di ModifierKeys.

Affinché un InputGesture agisca come origine del comando, deve essere associato a un comando. Esistono alcuni modi per eseguire questa operazione. Un modo consiste nell'usare un InputBinding.

Nell'esempio seguente viene illustrato come creare un KeyBinding tra un KeyGesture e un 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)

Un altro modo per associare un InputGesture a un RoutedCommand consiste nell'aggiungere il InputGesture al InputGestureCollection nel RoutedCommand.

Nell'esempio seguente viene illustrato come aggiungere un KeyGesture alla InputGestureCollection di un 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)

CommandBinding

Un CommandBinding associa un comando ai gestori eventi che implementano il comando .

La classe CommandBinding contiene una proprietà Command e gli eventi PreviewExecuted, Executed, PreviewCanExecutee CanExecute.

Command è il comando a cui è associato il CommandBinding. I gestori di eventi che sono collegati agli eventi PreviewExecuted e Executed implementano la logica del comando. I gestori eventi collegati agli eventi PreviewCanExecute e CanExecute determinano se il comando può essere eseguito sul bersaglio del comando corrente.

Nell'esempio seguente viene illustrato come creare un CommandBinding sulla radice Window di un'applicazione. L'CommandBinding associa il comando Open ai gestori Executed e CanExecute.

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

Vengono quindi creati i ExecutedRoutedEventHandler e un CanExecuteRoutedEventHandler. Il ExecutedRoutedEventHandler apre un MessageBox che visualizza una stringa che indica che il comando è stato eseguito. Il CanExecuteRoutedEventHandler imposta la proprietà CanExecute su 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

Un CommandBinding è collegato a un oggetto specifico, ad esempio alla radice Window dell'applicazione o a un controllo. L'oggetto a cui è associato il CommandBinding definisce l'ambito dell'associazione. Ad esempio, un CommandBinding collegato a un predecessore della destinazione del comando può essere raggiunto dall'evento Executed, ma non è possibile raggiungere un CommandBinding collegato a un discendente della destinazione del comando. Si tratta di una conseguenza diretta del modo in cui un RoutedEvent si muove e si propaga dall'oggetto che genera l'evento.

In alcune situazioni il CommandBinding viene collegato alla destinazione del comando stessa, ad esempio con la classe TextBox e i comandi Cut, Copye Paste. Molto spesso, tuttavia, è più conveniente collegare il CommandBinding a un predecessore della destinazione del comando, ad esempio il Window principale o l'oggetto Application, soprattutto se la stessa CommandBinding può essere usata per più destinazioni di comando. Si tratta di decisioni di progettazione da prendere in considerazione quando si crea l'infrastruttura di comando.

Destinazione comando

La destinazione del comando è l'elemento in cui viene eseguito il comando. Per quanto riguarda un RoutedCommand, la destinazione del comando è l'elemento in corrispondenza del quale viene avviato il routing del Executed e CanExecute. Come indicato in precedenza, in WPF la proprietà CommandTarget su ICommandSource è applicabile solo quando il ICommand è un RoutedCommand. Se il CommandTarget è impostato su un ICommandSource e il comando corrispondente non è un RoutedCommand, la destinazione del comando viene ignorata.

L'origine del comando può impostare in modo esplicito la destinazione del comando. Se la destinazione del comando non è definita, l'elemento con lo stato attivo della tastiera verrà usato come destinazione del comando. Uno dei vantaggi dell'utilizzo dell'elemento con focus della tastiera come destinazione del comando è che consente allo sviluppatore dell'applicazione di usare la stessa origine dei comandi per inviare un comando a più destinazioni senza dover tenere traccia della destinazione del comando. Ad esempio, se un MenuItem richiama il comando Incolla in un'applicazione con un controllo TextBox e un controllo PasswordBox, la destinazione può essere il TextBox o il PasswordBox a seconda del controllo che ha il focus della tastiera.

Nell'esempio seguente viene illustrato come impostare in modo esplicito la destinazione del comando nel markup e nel code-behind.

<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

Il CommandManager

Il CommandManager serve una serie di funzioni correlate ai comandi. Fornisce un set di metodi statici per l'aggiunta e la rimozione dei gestori di eventi PreviewExecuted, Executed, PreviewCanExecutee CanExecute su un elemento specifico. Fornisce un mezzo per registrare gli oggetti CommandBinding e InputBinding su una classe specifica. Il CommandManager fornisce anche un mezzo, tramite l'evento RequerySuggested, per notificare a un comando quando deve generare l'evento CanExecuteChanged.

Il metodo InvalidateRequerySuggested forza il CommandManager a generare l'evento RequerySuggested. Ciò è utile per le condizioni che devono disabilitare/abilitare un comando, ma non sono condizioni di cui è a conoscenza il CommandManager.

Libreria dei comandi

WPF fornisce un set di comandi predefiniti. La libreria dei comandi è costituita dalle classi seguenti: ApplicationCommands, NavigationCommands, MediaCommands, EditingCommandse il ComponentCommands. Queste classi forniscono comandi come Cut, BrowseBack e BrowseForward, Play, Stope Pause.

Molti di questi comandi includono un set di associazioni di input predefinite. Ad esempio, se si specifica che l'applicazione gestisce il comando di copia, si ottiene automaticamente l'associazione da tastiera "CTRL+C" Si ottengono anche associazioni per altri dispositivi di input, ad esempio i movimenti della penna del TABLET PC e le informazioni vocali.

Quando si fa riferimento ai comandi nelle varie librerie di comandi usando XAML, in genere è possibile omettere il nome della classe di libreria che espone la proprietà del comando statico. In genere, i nomi dei comandi non sono ambigui come stringhe e i tipi proprietari esistono per fornire un raggruppamento logico di comandi, ma non sono necessari per la disambiguazione. Ad esempio, è possibile specificare Command="Cut" anziché il più prolisso Command="ApplicationCommands.Cut". Si tratta di un meccanismo pratico integrato nel processore XAML WPF per i comandi (più precisamente, è un comportamento del convertitore di tipi di ICommand, a cui fa riferimento il processore XAML WPF in fase di caricamento).

Creazione di comandi personalizzati

Se i comandi nelle classi della libreria dei comandi non soddisfano le proprie esigenze, è possibile creare comandi personalizzati. Esistono due modi per creare un comando personalizzato. Il primo consiste nell'iniziare da zero e implementare l'interfaccia ICommand. L'altro modo e l'approccio più comune consiste nel creare un RoutedCommand o un RoutedUICommand.

Per un esempio di creazione di un RoutedCommandpersonalizzato, vedere Creare un esempio routedCommand personalizzato.

Vedere anche