Cenni preliminari sull'esecuzione di comandi

Il comando è un meccanismo di input in Windows Presentation Foundation (WPF) che fornisce la gestione dell'input a un livello più semantico rispetto all'input del dispositivo. Sono esempi di comandi le operazioni Copia, Taglia e Incolla disponibili 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 include le sezioni seguenti:

Che cosa sono i comandi

I comandi hanno diversi scopi. Il primo scopo è di separare la semantica e l'oggetto che richiama un comando dalla logica che esegue il comando. In questo modo, più codici sorgente diversi possono richiamare la stessa logica di comando che può quindi essere personalizzata per obiettivi differenti. Le operazioni di modifica Copia, Taglia e Incolla, ad esempio, disponibili in molte applicazioni, possono essere richiamate usando azioni dell'utente diverse se vengono implementate tramite i comandi. Un'applicazione potrebbe consentire a un utente di tagliare gli oggetti o il testo selezionati facendo clic su un pulsante, scegliendo una voce da un menu o usando una combinazione di tasti, ad esempio CTRL+X. Usando i comandi è possibile associare ogni tipo di azione utente alla stessa logica.

Un altro scopo dei comandi è di indicare se un'azione è disponibile. Per continuare con l'esempio in cui viene tagliato un oggetto o un testo, l'azione ha senso solo quando viene selezionato un elemento. Se un utente tenta di tagliare un oggetto o un testo senza avere selezionato alcun elemento, non viene eseguita alcuna operazione. Per indicarlo all'utente, in molte applicazioni vengono disabilitati i pulsanti e le 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 oppure essere abilitato se CanExecute restituisce true.

La semantica di un comando può essere coerente tra le applicazioni e le classi, tuttavia la logica dell'azione è specifica dell'oggetto particolare su cui si agisce. La combinazione di tasti CTRL+X richiama il comando Taglia nelle classi di testo, nelle classi di immagine e nei Web browser, tuttavia la logica effettiva per l'esecuzione dell'operazione Taglia viene definita dall'applicazione in cui si esegue l'operazione di taglio. RoutedCommand consente ai client di implementare la logica. Un oggetto testo può tagliare il testo selezionato negli Appunti, mentre un oggetto immagine può tagliare l'immagine selezionata. Quando un'applicazione gestisce l'evento Executed, può accedere alla destinazione del comando ed eseguire l'azione appropriata in base al tipo della destinazione.

Esempio di comando semplice in WPF

Il modo più semplice per usare un comando in WPF consiste nell'usare un oggetto predefinito RoutedCommand da una delle classi della libreria di 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 della classe ApplicationCommands. Il controllo TextBox è dotato di logica incorporata per la gestione del comando Paste. E la classe MenuItem è dotata del supporto nativo per la chiamata di comandi.

L'esempio seguente illustra come impostare un oggetto MenuItem in modo che quando si fa clic su di esso, richiami il comando Paste in un TextBox, supponendo che 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 dell'esecuzione dei comandi 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 su cui viene eseguito il comando.

  • L'associazione del comando è l'oggetto che esegue il mapping della logica di comando al comando.

Nell'esempio precedente, il comando Paste corrisponde al comando, MenuItem è l'origine del comando, TextBox è la destinazione del comando e l'associazione del comando viene fornita dal controllo TextBox. È importante notare che non sempre l'oggetto CommandBinding viene fornito dal controllo che rappresenta la classe di destinazione del comando. Molto spesso è lo sviluppatore dell'applicazione che deve creare CommandBinding. In altri casi, può esistere un collegamento tra CommandBinding e un predecessore della destinazione del comando.

Comandi

I comandi in WPF vengono creati implementando l'interfaccia ICommand . ICommand espone due metodi, Execute e CanExecute, e un evento, CanExecuteChanged. Execute esegue le azioni associate al comando. CanExecute determina se il comando può essere eseguito nella destinazione corrente del comando stesso. CanExecuteChanged viene generato se il gestore 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 del comando. L'implementazione WPF di ICommand è la RoutedCommand classe ed è l'obiettivo di questa panoramica.

Le origini principali di input in WPF sono il mouse, la tastiera, l'input penna e i comandi indirizzati. La maggior parte degli input orientati al dispositivo usano un oggetto RoutedEvent per notificare agli oggetti di una pagina dell'applicazione che si è verificato un evento di input. RoutedCommand non è diverso. I metodi Execute e CanExecute di RoutedCommand non contengono la logica dell'applicazione per il comando, ma generano eventi indirizzati che effettuano il tunneling e il bubbling attraverso la struttura ad albero degli elementi finché non rilevano un oggetto con CommandBinding. CommandBinding contiene i gestori per questi eventi e sono i gestori a eseguire il comando. Per altre informazioni sul routing degli eventi in WPF, vedere Cenni preliminari sugli eventi indirizzati.

Il metodo Execute in un RoutedCommand genera gli eventi PreviewExecuted e Executed nella destinazione del comando. Il metodo CanExecute in un RoutedCommand genera gli eventi CanExecute e PreviewCanExecute nella destinazione del comando. Questi eventi eseguono il tunneling e il bubbling nell'albero degli elementi finché non incontrano un oggetto con un CommandBinding per quel particolare comando.

WPF fornisce un set di comandi indirizzati comuni distribuiti tra diverse classi: MediaCommands, ApplicationCommandsNavigationCommands, ComponentCommands, e EditingCommands. Queste classi sono costituite solo da oggetti RoutedCommand e non dalla logica di implementazione del comando. La logica di implementazione è responsabilità dell'oggetto sul quale viene eseguito il comando.

Origini dei comandi

Un'origine del comando è l'oggetto che richiama il comando. MenuItem, Button e KeyGesture sono esempi di origini del comando.

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

ICommandSource espone tre proprietà: Command, CommandTarget e CommandParameter:

Le classi WPF che implementano ICommandSource sono ButtonBase, MenuItem, Hyperlinke InputBinding. ButtonBase, MenuItem e Hyperlink richiamano un comando quando ricevono un clic e InputBinding richiama un comando quando l'oggetto InputGesture associato viene eseguito.

L'esempio seguente illustra come usare un oggetto MenuItem in un oggetto ContextMenu come origine del 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

Un'origine del comando è in genere in ascolto dell'evento CanExecuteChanged. Questo evento informa l'origine del comando relativamente alla probabile modifica della capacità del comando di essere eseguito sulla destinazione del comando corrente. L'origine del comando può eseguire una query dello stato corrente dell'oggetto RoutedCommand tramite il metodo CanExecute. L'origine comando può quindi disabilitarsi, se l'esecuzione del comando non riesce. Un esempio è un oggetto MenuItem che si disattiva quando non è possibile eseguire il comando corrispondente.

Un oggetto InputGesture può essere usato come origine del comando. Due tipi di movimenti di input in WPF sono e KeyGestureMouseGesture. È possibile considerare un oggetto KeyGesture come 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 di ModifierKeys facoltativo.

Affinché un oggetto InputGesture funzioni come origine del comando, deve essere associato a un comando. Questa operazione può essere eseguita in diversi modi. Un modo consiste nell'uso di un oggetto InputBinding.

L'esempio seguente illustra 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 l'oggetto InputGesture a InputGestureCollection per RoutedCommand.

L'esempio seguente illustra come aggiungere un KeyGesture alla raccolta 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

Una classe CommandBinding associa un comando ai gestori degli eventi che implementano il comando.

La classe CommandBinding contiene la proprietà Command e gli eventi PreviewExecuted, Executed, PreviewCanExecute e CanExecute.

Command è il comando a cui CommandBinding è associato. I gestori degli eventi associati agli eventi PreviewExecuted e Executed implementano la logica di comando. I gestori eventi associati agli eventi PreviewCanExecute e CanExecute determinano se il comando può essere eseguito nella destinazione corrente.

L'esempio seguente illustra come creare un CommandBinding in Window radice di un'applicazione. 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)

Successivamente vengono creati ExecutedRoutedEventHandler e CanExecuteRoutedEventHandler. ExecutedRoutedEventHandler apre un oggetto MessageBox che visualizza una stringa che indica che il comando è stato eseguito. Il metodo 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 oggetto CommandBinding viene collegato a un oggetto specifico, ad esempio all'oggetto Window radice dell'applicazione o di un controllo. L'oggetto a cui è collegato l'oggetto CommandBinding definisce l'ambito dell'associazione. È ad esempio possibile raggiungere un oggetto CommandBinding collegato a un predecessore della destinazione del comando tramite l'evento Executed, ma un oggetto CommandBinding collegato a un discendente della destinazione del comando non è raggiungibile. Si tratta di una conseguenza diretta del modo in cui un oggetto RoutedEvent effettua il tunneling e il bubbling dall'oggetto che genera l'evento.

In alcune situazioni l'oggetto CommandBinding è collegato alla destinazione del comando stessa, ad esempio con la classe TextBox e i comandi Cut, Copy e Paste. Tuttavia, spesso è consigliabile associare l'oggetto CommandBinding a un predecessore della destinazione del comando, ad esempio l'oggetto Window principale o l'oggetto Application, specialmente se lo stesso oggetto CommandBinding può essere usato per più destinazioni di comandi. Si tratta di decisioni di progettazione di cui tenere conto quando si crea l'infrastruttura di esecuzione dei comandi.

Destinazione del comando

La destinazione del comando è l'elemento su cui viene eseguito il comando. Per quanto riguarda RoutedCommand, la destinazione del comando è l'elemento in corrispondenza del quale inizia il routing di Executed e CanExecute. Come indicato in precedenza, in WPF la CommandTarget proprietà su ICommandSource è applicabile solo quando ICommand è .RoutedCommand Se 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 viene definita, l'elemento con lo stato attivo verrà usato come destinazione del comando. Uno dei vantaggi dell'uso dell'elemento con lo stato attivo come destinazione del comando consiste nel fatto che consente allo sviluppatore di applicazioni di usare la stessa origine del comando per richiamare un comando su più destinazioni senza tenere traccia della destinazione del comando. Se, ad esempio, un oggetto MenuItem richiama il comando Incolla in un'applicazione che ha un controllo TextBox e un controllo PasswordBox, la destinazione può essere l'oggetto TextBox o l'oggetto PasswordBox a seconda del controllo con stato attivo.

L'esempio seguente mostra 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

CommandManager

CommandManager svolge un certo numero di funzioni correlate a comandi. Rende disponibile un set di metodi statici per l'aggiunta e la rimozione di gestori degli eventi PreviewExecuted, Executed, PreviewCanExecute e CanExecute da e verso un elemento specifico. Rappresenta un modo per registrare oggetti CommandBinding e InputBinding in una classe specifica. CommandManager consente anche, tramite l'evento RequerySuggested, di notificare a un comando quando deve essere generato l'evento CanExecuteChanged.

Il metodo InvalidateRequerySuggested impone a CommandManager di generare l'evento RequerySuggested. Ciò è utile per le condizioni che richiedono l'abilitazione o la disabilitazione di un comando ma che l'oggetto CommandManager non è in grado di rilevare.

Libreria dei comandi

WPF fornisce un set di comandi predefiniti. La libreria dei comandi è costituita dalle classi seguenti: ApplicationCommands, NavigationCommands, MediaCommands, EditingCommands e ComponentCommands. Queste classi forniscono comandi quali Cut, BrowseBack e BrowseForward, Play, Stop e 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. Di solito i nomi dei comandi sono stringhe non ambigue ed esistono tipi proprietari che forniscono un raggruppamento logico di comandi, ma che non sono necessari per la risoluzione dell'ambiguità. Ad esempio, è possibile specificare Command="Cut" anziché il più dettagliato Command="ApplicationCommands.Cut". Si tratta di un meccanismo pratico integrato nel processore XAML WPF per i comandi (più precisamente, si tratta di un comportamento del convertitore di tipi di , a cui fa riferimento il processore XAML WPF in fase di ICommandcaricamento).

Creazione di comandi personalizzati

Se i comandi delle classi della libreria dei comandi non soddisfano le proprie esigenze, è possibile creare comandi personalizzati. Questa operazione può essere eseguita in due modi. Il primo prevede la progettazione da zero e l'implementazione dell'interfaccia ICommand. L'altro, che rappresenta l'approccio più comune, consiste nel creare un oggetto RoutedCommand o RoutedUICommand.

Per un esempio di creazione di un oggetto RoutedCommand personalizzato, vedere Create a Custom RoutedCommand Sample (Creare un esempio di oggetto RoutedCommand personalizzato).

Vedi anche