Cenni preliminari sull'esecuzione di comandi
L'esecuzione di comandi è un meccanismo di input di Windows Presentation Foundation (WPF) che fornisce la gestione di input a un livello semantico maggiore rispetto all'input del dispositivo. Le operazioni Copia, Taglia e Incolla presenti in molte applicazioni sono esempi di comandi.
In questi cenni preliminari vengono definiti i comandi presenti in WPF, le classi che fanno parte del modello di esecuzione di comandi e la modalità di utilizzo e di creazione di comandi all'interno delle applicazioni.
Di seguito sono elencate le diverse sezioni di questo argomento:
Definizione dei comandi
Esempio di comando semplice in WPF
Quattro concetti principali relativi all'esecuzione di comandi in WPF
Libreria dei comandi
Creazione di comandi personalizzati
Definizione dei 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 pertanto può essere personalizzata per obiettivi differenti. Le operazioni di modifica Copia, Taglia e Incolla, ad esempio, disponibili in molte applicazioni, possono essere richiamate utilizzando azioni dell'utente diverse se vengono implementati tramite i comandi. Un'applicazione potrebbe consentire a un utente di tagliare gli oggetti o il testo selezionato facendo clic su un pulsante, scegliendo una voce da un menu o utilizzando una combinazione di tasti, ad esempio CTRL+X. Utilizzando 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. Un oggetto 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, dispone di accesso alla destinazione del comando e può eseguire l'azione appropriata in base al tipo della destinazione.
Esempio di comando semplice in WPF
Il modo più semplice per utilizzare un comando in WPF consiste nell'utilizzo di un oggetto RoutedCommand predefinito di una delle classi della libreria dei comandi; nell'utilizzare un controllo che dispone del supporto nativo per gestire il comando; nell'utilizzare un controllo che dispone del supporto nativo per richiamare un comando. Il comando Paste è uno dei comandi predefiniti della classe ApplicationCommands. Il controllo TextBox dispone di una logica incorporata per la gestione del comando Paste. La classe MenuItem dispone del supporto nativo per richiamare i comandi.
Nell'esempio seguente viene mostrato come configurare un oggetto MenuItem in modo che, una volta selezionato, richiami il comando Paste su un oggetto TextBox, presupponendo che l'oggetto TextBox disponga dello stato attivo.
<StackPanel>
<Menu>
<MenuItem Command="ApplicationCommands.Paste" />
</Menu>
<TextBox />
</StackPanel>
' 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
// 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;
Quattro concetti principali relativi all'esecuzione di comandi in WPF
Il modello di comando indirizzato di WPF può essere suddiviso in quattro concetti principali: il comando, l'origine comando, la destinazione comando e l'associazione comando:
Il comando è l'azione da eseguire.
L'origine comando è l'oggetto che richiama il comando.
La destinazione comando è l'oggetto sul quale il comando viene eseguito.
L'associazione comando è l'oggetto che esegue il mapping della logica di comando al comando.
Nell'esempio precedente, il comando Paste rappresenta il comando, l'oggetto MenuItem rappresenta l'origine comando, l'oggetto TextBox rappresenta la destinazione comando e l'associazione comando viene fornita dal controllo TextBox. È importante notare che non sempre l'oggetto CommandBinding viene fornito dal controllo che rappresenta la classe di destinazione comando. Spesso, l'oggetto CommandBinding deve essere creato dallo sviluppatore di applicazioni oppure può essere associato a un predecessore della destinazione 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 sia possibile eseguirlo sulla destinazione corrente. Viene generato CanExecuteChangedse 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 dell'oggetto ICommand è la classe RoutedCommand e rappresenta l'oggetto di discussione principale di questi cenni preliminari.
Le origini di input principali di WPF sono il mouse, la tastiera, l'input penna e i comandi indirizzati. La maggior parte degli input orientati al dispositivo utilizzano un oggetto RoutedEvent per notificare agli oggetti di una pagina dell'applicazione che si è verificato un evento di input. Un oggetto RoutedCommand non è diverso. I metodi Execute e CanExecute di un oggetto RoutedCommand non contengono la logica dell'applicazioni per il comando, piuttosto generano eventi indirizzati che effettuano il tunneling e il bubbling della struttura ad albero dell'elemento, finché non incontrano un oggetto con CommandBinding. Nell'oggetto CommandBinding sono contenuti i gestori di questi eventi, i quali eseguono il comando. Per ulteriori informazioni sul routing degli eventi in WPF, vedere Cenni preliminari sugli eventi indirizzati.
Il metodo Execute di un oggetto RoutedCommand genera gli eventi PreviewExecuted e Executed sulla destinazione comando. Il metodo CanExecute di un oggetto RoutedCommand genera gli eventi CanExecute e PreviewCanExecute sulla destinazione comando. Tali eventi eseguono il tunneling e il bubbling nella struttura ad albero dell'elemento finché non incontrano un oggetto che dispone di un oggetto CommandBinding per quel particolare comando.
In WPF viene fornito un set di comandi indirizzati comuni contenuti in più classi: MediaCommands, ApplicationCommands, NavigationCommands, 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 il comando viene eseguito.
Origini comando
Un origine comando è l'oggetto che richiama il comando. MenuItem, Button e KeyGesture sono esempi di origini comando.
In genere, le origini comando di WPF implementano l'interfaccia ICommandSource.
L'oggetto ICommandSource espone tre proprietà: Command, CommandTarget e CommandParameter:
La proprietà Command è il comando da eseguire quando viene richiamata l'origine comando.
La proprietà CommandTarget è l'oggetto su cui eseguire il comando. È importante notare che in WPF la proprietà CommandTarget di ICommandSource può essere applicata solo quando l'oggetto ICommand è un oggetto RoutedCommand. Se la proprietà CommandTarget viene impostata sull'oggetto ICommandSource e il comando corrispondente non è un oggetto RoutedCommand, la destinazione comando viene ignorata. Se la proprietà CommandTarget non viene impostata, l'elemento con lo stato attivo sarà la destinazione comando.
L'oggetto CommandParameter è un tipo di dati definito dall'utente utilizzato per passare informazioni ai gestori che implementano il comando.
Le classi WPF che implementano l'oggetto ICommandSource sono: ButtonBase, MenuItem, Hyperlink e InputBinding. Gli oggetti ButtonBase, MenuItem e Hyperlink richiamano un comando quando vengono selezionati, mentre l'oggetto InputBinding richiama un comando quando viene eseguito l'oggetto InputGesture associato.
Nell'esempio seguente viene illustrato come utilizzare MenuItem in ContextMenu come un'origine comando per il comando Properties.
<StackPanel>
<StackPanel.ContextMenu>
<ContextMenu>
<MenuItem Command="ApplicationCommands.Properties" />
</ContextMenu>
</StackPanel.ContextMenu>
</StackPanel>
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
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;
In genere, un'origine comando resterà in ascolto dell'evento CanExecuteChanged. Tale evento informa l'origine comando relativamente alla probabile modifica della capacità del comando di essere eseguito sulla destinazione comando corrente. L'origine comando può eseguire una query sullo stato corrente dell'oggetto RoutedCommand utilizzando il metodo CanExecute. L'origine comando può quindi disabilitarsi, se l'esecuzione del comando non riesce. Un esempio viene fornito dal fatto che l'oggetto MenuItem diventa di colore grigio quando non è possibile eseguire un comando.
Un oggetto InputGesture può essere utilizzato come origine comando. Gli oggetti KeyGesture e MouseGesture sono due tipi di movimento di input di WPF. È possibile considerare un oggetto KeyGesture come un tasto di scelta rapida, ad esempio CTRL+C. Un oggetto KeyGesture è costituito da un oggetto Key e da un insieme di oggetti ModifierKeys. Un oggetto MouseGesture è costituito da un oggetto MouseAction e da un insieme facoltativo di oggetti ModifierKeys.
Affinché un oggetto InputGesture funzioni come origine comando, deve essere associato a un comando. Questa operazione può essere eseguita in diversi modi. Uno di questi consiste nell'utilizzare un oggetto InputBinding.
Nell'esempio seguente viene illustrato come creare un oggetto KeyBinding tra un oggetto KeyGesture e un oggetto RoutedCommand.
<Window.InputBindings>
<KeyBinding Key="B"
Modifiers="Control"
Command="ApplicationCommands.Open" />
</Window.InputBindings>
Dim OpenKeyGesture As New KeyGesture(Key.B, ModifierKeys.Control)
Dim OpenCmdKeybinding As New KeyBinding(ApplicationCommands.Open, OpenKeyGesture)
Me.InputBindings.Add(OpenCmdKeybinding)
KeyGesture OpenKeyGesture = new KeyGesture(
Key.B,
ModifierKeys.Control);
KeyBinding OpenCmdKeybinding = new KeyBinding(
ApplicationCommands.Open,
OpenKeyGesture);
this.InputBindings.Add(OpenCmdKeybinding);
Un altro modo per associare un oggetto InputGesture a un oggetto RoutedCommand consiste nell'aggiungere InputGesture all'oggetto InputGestureCollection nell'oggetto RoutedCommand.
Nell'esempio seguente viene illustrato come aggiungere un oggetto KeyGesture all'oggetto InputGestureCollection di un oggetto RoutedCommand.
Dim OpenCmdKeyGesture As New KeyGesture(Key.B, ModifierKeys.Control)
ApplicationCommands.Open.InputGestures.Add(OpenCmdKeyGesture)
KeyGesture OpenCmdKeyGesture = new KeyGesture(
Key.B,
ModifierKeys.Control);
ApplicationCommands.Open.InputGestures.Add(OpenCmdKeyGesture);
CommandBinding
Un oggetto CommandBinding associa un comando ai gestori eventi che implementano il comando.
La classe CommandBinding contiene una proprietà Command e gli eventi PreviewExecuted, Executed, PreviewCanExecute e CanExecute.
L'oggetto Command è il comando a cui è associato l'oggetto CommandBinding. I gestori 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 comando corrente.
Nell'esempio seguente viene mostrato come creare un oggetto CommandBinding nella radice Window di un'applicazione. L'oggetto 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
Dim OpenCmdBinding As New CommandBinding(ApplicationCommands.Open, AddressOf OpenCmdExecuted, AddressOf OpenCmdCanExecute)
Me.CommandBindings.Add(OpenCmdBinding)
// Creating CommandBinding and attaching an Executed and CanExecute handler
CommandBinding OpenCmdBinding = new CommandBinding(
ApplicationCommands.Open,
OpenCmdExecuted,
OpenCmdCanExecute);
this.CommandBindings.Add(OpenCmdBinding);
Successivamente, vengono creati gli oggetti ExecutedRoutedEventHandler e CanExecuteRoutedEventHandler. ExecutedRoutedEventHandler consente di aprire un oggetto MessageBox nel quale viene visualizzata una stringa che conferma l'esecuzione del comando. L'oggetto CanExecuteRoutedEventHandler imposta la proprietà CanExecute su true.
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 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 OpenCmdCanExecute(ByVal sender As Object, ByVal e As CanExecuteRoutedEventArgs)
e.CanExecute = True
End Sub
void OpenCmdCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
Un oggetto CommandBinding viene associato a un oggetto specifico, ad esempio la radice Window dell'applicazione o di un controllo. L'oggetto a cui è associato l'oggetto CommandBinding definisce l'ambito dell'associazione. Ad esempio, un oggetto CommandBinding associato a un predecessore della destinazione comando può essere raggiunto dall'evento Executed, mentre un oggetto CommandBinding associato a un discendente della destinazione comando non può essere raggiunto. 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 viene associato alla destinazione comando stessa, ad esempio con la classe TextBox e i comandi Cut, Copye Paste. Tuttavia, spesso è consigliabile associare l'oggetto CommandBinding a un predecessore della destinazione comando, ad esempio l'oggetto Window principale o l'oggetto Application, specialmente se lo stesso oggetto CommandBinding può essere utilizzato per più destinazioni comando. Si tratta di decisioni di progettazione di cui tenere conto quando si crea l'infrastruttura di esecuzione dei comandi.
Destinazione comando
La destinazione comando è l'elemento sul quale viene eseguito il comando. Per quanto riguarda un oggetto RoutedCommand, la destinazione comando è l'elemento a partire dal quale viene avviato il routing degli eventi Executed e CanExecute. Come indicato in precedenza, in WPF la proprietà CommandTarget dell'oggetto ICommandSource può essere applicata solo se l'oggetto ICommand è un oggetto RoutedCommand. Se l'oggetto CommandTarget viene impostato su ICommandSource e il comando corrispondente non è un oggetto RoutedCommand, la destinazione comando viene ignorata.
L'origine comando può impostare in modo esplicito la destinazione comando. Se quest'ultima non viene definita, l'elemento con lo stato attivo sarà utilizzato come destinazione comando. Uno dei vantaggi dell'utilizzo dell'elemento con lo stato attivo come destinazione comando consiste nel fatto che consente allo sviluppatore di applicazioni di utilizzare la stessa origine comando per richiamare un comando su più destinazioni senza tenere traccia della destinazione comando. Se, ad esempio, l'oggetto MenuItem richiama il comando Incolla in un'applicazione che dispone di un controllo TextBox e di un controllo PasswordBox, la destinazione può essere l'oggetto TextBox oppure l'oggetto PasswordBox, in base al controllo che dispone dello stato attivo.
Nell'esempio seguente viene mostrato come impostare in modo esplicito la destinazione 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
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
// 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;
CommandManager
L'oggetto CommandManager è utile per molte funzioni relative ai comandi. Rende disponibile un insieme di metodi statici per aggiungere e rimuovere i gestori eventi PreviewExecuted, Executed, PreviewCanExecute e CanExecute da un elemento specifico nonché un modo per registrare gli oggetti CommandBinding e InputBinding in una classe specifica. L'oggetto CommandManager fornisce inoltre un modo, tramite l'evento RequerySuggested, per notificare a un comando il momento in cui deve generare l'evento CanExecuteChanged.
Il metodo InvalidateRequerySuggested forza l'oggetto 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
In WPF viene fornito un insieme di comandi predefiniti. La libreria dei comandi include le classi seguenti: ApplicationCommands, NavigationCommands, MediaCommands, EditingCommands e ComponentCommands. Tali classi forniscono comandi, quali Cut, BrowseBack e BrowseForward, Play, Stop e Pause.
Molti di questi comandi includono un insieme di associazioni di input predefinite. Se, ad esempio, si specifica che l'applicazione gestisce il comando di copia, si ottiene automaticamente l'associazione di tastiera "CTRL + C". Si ottengono inoltre associazioni per altri dispositivi di input, quali i movimenti della penna Tablet PC e informazioni sulle funzioni vocali.
Quando si fa riferimento ai comandi delle diverse librerie dei comandi utilizzando XAML, in genere è possibile omettere il nome classe 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à. È possibile, ad esempio, specificare Command="Cut" anziché l'oggetto Command="ApplicationCommands.Cut" più dettagliato. Si tratta di un meccanismo utile incorporato nel processore XAML WPF per i comandi (più precisamente, si tratta di un comportamento del convertitore dei tipi di ICommand al quale il processore XAML WPF fa riferimento in fase di caricamento).
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. Il secondo, che costituisce l'approccio più comune, consiste nel creare un oggetto RoutedCommand o un oggetto RoutedUICommand.
Per un esempio di creazione di un oggetto RoutedCommand personalizzato, vedere Esempio di creazione di un RoutedCommand personalizzato (la pagina potrebbe essere in inglese).
Vedere anche
Attività
Procedura: implementare ICommandSource
How to: Add a Command to a MenuItem
Riferimenti
Concetti
Cenni preliminari sugli eventi indirizzati