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 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 servono Un 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 ...

VB.NET: SplashScreen con effetto fade-in

In questo post vorrei proporvi un modo per realizzare una splash screen per le nostre applicazioni Windows Form che appare progressivamente con un effetto fade. Supponiamo di avere il nostro progetto VB.NET in una soluzione Visual Studio 2008 in cui abbiamo il sorgente della nostra applicazione Windows Form. Inseriamo una splash screen utilizzando il menù Progetto->Aggiungi Nuovo Elemento e selezionando il tipo di elemento “Schermata Iniziale” A questo punto Visual Studio creerà, automaticamente, la schermata iniziale che possiamo personalizzare graficamente come vogliamo. Per poter fare in modo che questa finestra appaia nel momento in cui avviamo l’applicazione, è necessario aprire le proprietà del progetto e impostare la maschera di avvio: In questo modo, all’avvio dell’applicazione, la schermata appare immediatamente e scompare un attimo prima della visualizzazione della finestra dell’applicazione. Possiamo far apparire la schermata iniziale con un ef...

Alla scoperta del Kinect: presentazioni e convenevoli

Oggi è arrivato un Kinect nuovo nuovo su cui cominciare a fare sperimentazione ed ho, quindi, deciso di condividere tutto ciò che scopro, le cavolate che faccio e i segreti che scopro con chi mi segue. I post che cercherò di scrivere con frequenza sono post di un neofita che si avvicina all’”Aggeggio” e che quindi sono diretti a chi nulla dell’argomento. Gli esperti troveranno noiosi e banali questi post, ma non si può aver tutto dalla vita. Per cominciare, scartato l’”Aggeggio” ho cominciato a preparare l’ambiente di sviluppo: 1) Visual Studio 2010 Ultimate (che uso normalmente ma che non è necessario in quanto si può sviluppare tranquillamente con la express edition); 2) Kinect SDK, scaricabile all’indirizzo http://research.microsoft.com/en-us/um/redmond/projects/kinectsdk/download.aspx 3) DirectX Software Development Kit scaricabile all’indirizzo http://www.microsoft.com/download/en/details.aspx?displaylang=en&id=6812 A questo punto vi basta connettere il Kinect al...