Passa ai contenuti principali

Appunti di WPF – Quattordicesima Puntata – Comandi

WPF mette a disposizione una infrastruttura per la gestione dei comandi (cioè delle funzionalità che stanno dietro i controlli di una finestra applicativa) decisamente più evoluta rispetto a quella presente nelle Windows Forms in cui tale gestione è demandata ad un utilizzo oculato dei gestori di eventi.

In una applicazione ben progettata, il task applicativo che sta dietro ad un comando, non dovrebbe essere direttamente eseguito all’interno di un gestore di evento ma  in maniera disgiunta da questo.

Il comando applicativo, quindi, dovrebbe essere astratto rispetto al gestore di evento che lo gestisce in modo che sia molto facile fornire agli utenti nuovi controlli che eseguono lo stesso task.

Il modello dei comandi di WPF è composto dai seguenti pilastri:

· Commands : un comando rappresenta un task applicativo e contiene le informazioni necessarie per conoscere quando tale comando può essere eseguito o meno. Un comando non contiene il task vero e proprio (che, di solito, è contenuto in una apposita classe);

· Command Bindings : il command binding lega il comando all’effettiva logica applicativa. Grazie al command binding un comando può essere utilizzato in differenti punti della nostra applicazione;

· Sources : sono i controlli che generano il comando (button, menu, etc., etc.);

· Targets : sono gli elementi per cui il comando viene eseguito. Ad esempio l’apertura della lista delle stampanti in un comando Print.

L’interfaccia ICommand (contenuta nel namespace System.Windows.Command) definisce il comportamento di un comando. Questa interfaccia definisce i seguenti metodi ed eventi:

  1. Public Interface ICommand
  2.     ' Events
  3.     Custom Event CanExecuteChanged As EventHandler
  4.  
  5.     ' Methods
  6.     Function CanExecute(ByVal parameter As Object) As Boolean
  7.     Sub Execute(ByVal parameter As Object)
  8. End Interface

Il metodo Execute() contiene il task da eseguire mentre il metodo CanExecute() viene richiamato dal framework quando lo stesso framework ha la necessità di conoscere se il comando può essere eseguito o meno. Infine, l’evento CanExecuteChanged() viene invocato quando lo stato del comando cambia.

Possiamo definire un nostro comando implementando l’interfaccia ICommand ma possiamo utilizzare la classe RoutedCommand che ci mette a disposizione una serie di metodi accessori (come, ad esempio, la gestione del bubbling del comando che permette al comando di navigare la gerarchia dei controlli WPF fino a trovare l’opportuno gestore) che semplificano la realizzazione del comando.

Altra classe fondamentale per la gestione dei comandi è la RoutedUICommand che deriva dalla RoutedCommand ma che fornisce la possibilità di definire un testo del comando (con eventuale localizzazione del testo).

WPF_14_Comandi_Fig1

Il framework WPF ci mette a disposizione una serie di comandi predefiniti (che troviamo nella maggior parte delle applicazioni Windows) e che possono essere utilizzati senza dover usare direttamente la classe RoutedUICommand. Per accedere a questi comandi possiamo utilizzare le classi ApplicationCommands (per i normali comandi quali Copy, Paste, Open, New, etc., etc.), NavigationCommands (per i comandi di navigazione), EditingCommand (per i comandi di edit dei documenti), ComponentCommands e MediaCommands.

Vediamo ora, come eseguire un comando.

Per prima cosa abbiamo bisogno è una sorgente, cioè un oggetto che implementa l’interfaccia ICommandSource.

L’interfaccia ICommandSource prevede i seguenti membri:

· Command : oggetto ICommand che l’ICommandSource deve eseguire;

· CommandParameter : permette di inviare un qualsiasi oggetto al comando;

· CommandTarget : oggetto da cui il comando è effettivamente eseguito.

Il terzo pilastro per la gestione dei comandi in WPF è il CommandBinding. Il command binding permette di legare il comando al controllo o ai controlli che lo eseguono.

Cominciamo a vedere un esempio di interfaccia XAML con l’utilizzo di un comando:

  1. <Window x:Class="MainWindow"
  2.     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3.     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4.     Title="MainWindow" Height="350" Width="525">
  5.     <Window.CommandBindings>
  6.         <CommandBinding Command="Print"
  7.             Executed="PrintCommand_Executed"
  8.             CanExecute="PrintCommand_CanExecute"></CommandBinding>
  9.     </Window.CommandBindings>
  10.     <Grid>
  11.         <Grid.RowDefinitions>
  12.             <RowDefinition Height="20" />
  13.             <RowDefinition Height="*" />
  14.             <RowDefinition Height="Auto" />
  15.         </Grid.RowDefinitions>
  16.         <Menu Grid.Row="0">
  17.             <MenuItem Command="Print" VerticalAlignment="Center"></MenuItem >
  18.         </Menu>
  19.         <StackPanel Grid.Row="2">
  20.             <Button Command="Print"
  21.                 Content="{Binding Path=Command.Name, RelativeSource={RelativeSource Self}}"></Button>
  22.         </StackPanel>
  23.     </Grid>
  24. </Window>

Se eseguiamo l’applicazione, osserviamo che i due controlli sono disabilitati:

WPF_14_Comandi_Fig2Questo perché il framework, quando deve capire qual è lo stato del comando esegue il metodo PrintCommand_CanExecuted che, in questo caso, restituisce il valore di default (che è quello dello stato non abilitato).

Per attivare il comando ci basta scrivere il seguente codice:

  1. Private Sub PrintCommand_CanExecute(ByVal sender As System.Object,
  2.                                     ByVal e As System.Windows.Input.CanExecuteRoutedEventArgs)
  3.     e.CanExecute = True
  4. End Sub
  5.  
  6. Private Sub PrintCommand_Executed(ByVal sender As System.Object,
  7.                                   ByVal e As System.Windows.Input.ExecutedRoutedEventArgs)
  8.     MessageBox.Show("Print")
  9. End Sub

Il primo dei due gestori di evento comunica al framework che il comando è sempre attivo (e.CanExecute=true) mentre il secondo gestore esegue il comando.

Il risultato è il seguente:

WPF_14_Comandi_Fig3 In un’applicazione reale, lo stato del comando dipenderà dallo stato dell’applicazione.

I vantaggi sono notevoli: non dobbiamo implementare il codice che aggiorna lo stato dei controlli in base allo stato del comando, aggiungere nuovi controlli che eseguono lo stesso comando è una banalità e, infine, il task applicativo non è inserito nel gestore dell’evento dei controlli disaccoppiando il codice dall’interfaccia.

Inoltre, come possiamo osservare dal precedente XAML, anche il testo che appare nei controlli è standardizzato e un’eventuale localizzazione coinvolgerebbe tutti i controlli dello stesso comando in un colpo solo.

Il binding dei comandi si può eseguire anche da codice come mostrato di seguito:

  1. Dim binding = New CommandBinding(ApplicationCommands.Close)
  2. With binding
  3.     AddHandler .CanExecute, AddressOf Close_CanExecute
  4.     AddHandler .Executed, AddressOf Close_Executed
  5. End With

E’, possibile, inoltre eseguire un comando direttamente da codice utilizzando l’istruzione:

  1. ApplicationCommands.Print.Execute(Nothing, targetElement)

Esistono , infine, alcuni controlli che hanno già dei specifici comandi predefiniti. Un esempio di questo è il TextBox. Quando inseriamo il controllo TextBox in una pagina, abbiamo la possibilità di inserire dei comandi tipo Edit, Paste e Undo la cui attivazione è gestita direttamente dal framework in base al fatto che un utente selezioni del testo, abbia del testo nella clipboard o annulli l’ultima operazione. Ad esempio:

  1. <Window x:Class="Toolbar"
  2.     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3.     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4.     Title="Toolbar" Height="300" Width="300">
  5.     <Grid>
  6.         <Grid.RowDefinitions>
  7.             <RowDefinition Height="Auto"></RowDefinition>
  8.             <RowDefinition Height="*"></RowDefinition>
  9.         </Grid.RowDefinitions>
  10.         <ToolBar>
  11.             <Button Command="Copy">Copy</Button>
  12.             <Button Command="Paste">Paste</Button>
  13.             <Button Command="Undo">Undo</Button>
  14.         </ToolBar>
  15.         <StackPanel Grid.Row="1" Margin="10">
  16.             <TextBox>
  17.                 <TextBox.Background>
  18.                     <RadialGradientBrush>
  19.                         <GradientStop Color="cyan" Offset="1"></GradientStop>
  20.                         <GradientStop Color="white" Offset="0"></GradientStop>
  21.                     </RadialGradientBrush>
  22.                 </TextBox.Background>
  23.             </TextBox>
  24.         </StackPanel>
  25.     </Grid>
  26. </Window>

WPF_14_Comandi_Fig4

Per finire questo tutorial vediamo come creare rapidamente un comando custom.

Supponiamo di voler implementare il comando di Export. Definiamo la seguente classe:

  1. Public Class ExportCommand
  2.  
  3.     Private Shared _Export As RoutedUICommand
  4.  
  5.     Shared Sub New()
  6.         Dim inputs As New InputGestureCollection()
  7.         inputs.Add(New KeyGesture(Key.E, ModifierKeys.Control, "Ctrl+E"))
  8.         _Export = New RoutedUICommand("Export", "Export", GetType(ExportCommand), inputs)
  9.     End Sub
  10.  
  11.     Public Shared ReadOnly Property Export As RoutedUICommand
  12.         Get
  13.             Return _Export
  14.         End Get
  15.     End Property
  16.  
  17. End Class

Per utilizzare questo comando è necessario importare il namespace della nostra applicazione e impostare l’apposito binding come mostrato nel seguente XAML:

  1. <Window x:Class="Export"
  2.     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3.     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4.     xmlns:l="clr-namespace:WpfApplication"
  5.     Title="Export" Height="300" Width="300">
  6.     <Window.CommandBindings>
  7.         <CommandBinding Command="l:ExportCommand.Export"
  8.                         Executed="CommandBinding_Executed"
  9.                         CanExecute="CommandBinding_CanExecute"></CommandBinding>
  10.     </Window.CommandBindings>
  11.     <Grid>
  12.         <Button Command="l:ExportCommand.Export">Export</Button>
  13.     </Grid>
  14. </Window>


Scarica la versione PDF dell'articolo. Scarica la versione Amazon Kindle dell'articolo.

Commenti

Post popolari in questo blog

VB.NET: Convertire un file DOC in RTF e PDF con office interop

In questo post vorrei proporvi del codice per poter convertire un file .doc in un file .rtf oppure .pdf utilizzando le API di interoperabilità di Office.Creeremo una classe, DocConverter, che esporrà le due funzionalità sopra citate.Cominciamo con il prevedere un attributo privato della classe che rappresenterà l’applicazione Word che utilizzeremo per la conversione. Creeremo l’istanza dell’attributo privato all’interno del costruttore della classe:PublicSubNew()
IfNot CreateWordApp() Then
ThrowNew ApplicationException("Assembly di interoperabilità con Office non trovato!")
EndIf
EndSub
Private _wordApp As Word.ApplicationClass
ProtectedFunction CreateWordApp() AsBoolean
Dim retval = True
Try
_wordApp = New Word.ApplicationClass()
_wordApp.Visible = False
Catch ex As System.Exception
_wordApp = Nothing
retval = False
EndTry
Return retval
EndFunction

La conversione del file doc sarà effettuata aprendo il file stesso ed eseguendo un’operazione di SaveAs:

Pr…

VB.NET for Dummies: Gli Eventi

Vorrei iniziare con questo post una serie dedicata ad aspetti di VB.NET di base che possono essere utile a coloro che si avvicinano al mondo .NET e che, in genere, non vengono trattati a livello base.La serie di post non ha la pretesa di essere assolutamente esaustivi sugli argomenti che tratteròIn questo primo post parleremo degli eventi.Cosa sono e a cosa servonoUn evento è la “notifica” dell’accadimento di qualcosa.Quando, ad esempio, premiamo un bottone della finestra di un’applicazione, dietro le quinte, il bottone stesso “notifica” al mondo circostante che qualcuno, in quell’istante, lo ha premuto.Sta, poi, al mondo circostante preoccuparsi di “intercettare” l’evento stesso per gestirlo (tramite un gestore di evento).Attenzione a non confondere la pressione del tasto con la “notifica” della pressione del tasto: l’evento è la “notifica” dell’accadimento, non l’accadimento stesso.Ma a cosa serve tutto ciò?Utilizzare gli eventi è un modo per disaccoppiare due o più attori del nostr…

Creare uno shortcut con VB.NET

Prendendo spunto da un post comparso sul forum MSDN vorrei proporvi un tip su come creare uno shortcut utilizzando VB.NET.Per poter creare uno shortcut possiamo procedere in due modi: o ci studiamo la struttura del file .lnk e scriviamo una classe che è in grado di ricreare tale struttura oppure utilizziamo Windows Scripting Host.La prima soluzione è percorribile ma laboriosa perchè la struttura di un file lnk non è banale. Chi fosse interessato a vedere come è composto, internamente, un file lnk può scaricare la seguente reference guide (link).Io vorrei proporvi la seconda strada e realizzerò una classe che incapsula l’utilizzo di Windows Scripting Host.L’object model di Windows Scripting Host è contenuto nella dll IWshRuntimeLibrary che può essere referenziata, nel nostro progetto, utilizzando il tab COM della finestra di aggiunta delle reference:Tra gli oggetti che troviamo all’interno della libreria utilizzeremo la classe WshShell e la classe WshShortcut.La prima delle due rappres…