Nel forum di Visual Basic Tips & Tricks oggi è transitato un quesito molto interessante, ossia come utilizzare, in WPF, dei Button all’interno di un DataTemplate definito per le celle di una ListView disegnata a mo’ di griglia.
Facendo un discorso più astratto, si può dire che si tratta di stabilire come gestire eventi attraverso i DataTemplate. Premetto che quella che illustrerò è finora l’unica tecnica che ho provato, di conseguenza le segnalazioni dell’esistenza di tecniche migliori è ovviamente gradita.
Dobbiamo, in sostanza, riprendere il discorso dei CommandBindings che introducemmo tempo fa. I CommandBindings, infatti, ci permettono, per così dire, di lavorare in modo “astratto” sfruttando appieno il databinding.
Prendiamo in considerazione un esempio molto semplice, supponendo di avere una ListView in cui sia visualizzato l’elenco dei processi attivi. Nel DataTemplate stabiliamo che ciascuna riga sia costituita da un messaggio di testo riportante il nome del processo e da un pulsante al cui clic verrà visualizzato il nome del processo.
L’evento Click di un pulsante, come sappiamo, è di tipo Routed. Di conseguenza dobbiamo implementare un nuovo RoutedCommand. Dopo aver creato un nuovo progetto WPF per Visual Basic, aggiungiamo al progetto una nuova classe chiamata CommandHelper.vb. Una classe specifica ci permetterà, in futuro, di aggiungere ulteriori comandi.
All’interno di questa classe istanziamo un nuovo RoutedCommand, come segue:
Public Class CommandHelper
'Vogliamo che questo comando sia richiamato quando si fa Click sul pulsante
'e specifichiamo l'oggetto proprietario, in questo caso Window1
Public Shared SelectionCommand As New RoutedCommand("SelectedItem", GetType(Window1))
End Class
Ora compiliamo il progetto, di modo che possiamo aggiungere poi un riferimento all’assembly di primo livello nello XAML della finestra principale come segue (identificatore local):
<Window x:Class="Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:adsWpfCommandBindingsDataTemplate"
Title="Window1" Height="300" Width="300">
A livello di oggetto proprietario del RoutedCommand sopra definito, quindi la Window, dobbiamo dichiarare l’insieme di CommandBindings:
<Window.CommandBindings>
<CommandBinding Command="local:CommandHelper.SelectionCommand" Executed="OnItemSelected" />
</Window.CommandBindings>
Per ciascun CommandBinding va specificato, al livello più elementare, il comando (quello da noi precedentemente definito) e il gestore da richiamare al momento dell’esecuzione del comando stesso.
Ora implementiamo una ListView. Il codice, se avete seguito i miei precedenti articoli, è abbastanza familiare. Osservate, in modo particolare, i commenti inseriti all’interno:
<ListView ItemsSource="{Binding}" >
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
<ListView.View>
<GridView>
<GridViewColumn Header="Running processes" Width="150">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Text="{Binding Path=ProcessName}" Grid.Row="0"/>
<!--Specifico il Command rimandando a quello definito nel codice e passo
un dato ottenuto dal binding come argomento che il mio gestore potrà leggere-->
<Button Content="Click me!" Command="local:CommandHelper.SelectionCommand"
CommandParameter="{Binding ProcessName}" Grid.Row="1"/>
</Grid>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
Il codice sopra esposto non presenta particolari difficoltà se avete già avuto modo di affrontare l’utilizzo della ListView per presentare dei dati. La cosa interessante riguarda la dichiarazione del Button nel DataTemplate. La proprietà Command va ad identificare il routed event che dev’essere generato mentre la proprietà CommandParameter ci permette di inviare un valore che sarà poi visibile al gestore di evento, tramite binding. In questo specifico caso il gestore di evento sarà in grado di recepire il nome del processo associato alla riga in cui è presente il pulsante cliccato.
L’ultima modifica a livello di codice Visual Basic riguarda l’assegnazione dell’origine dati, che possiamo fare nel costruttore della finestra:
Public Sub New()
' This call is required by the Windows Form Designer.
InitializeComponent()
'Ottengo l'elenco di tutti i processi attivi e lo assegno alla proprietà
'DataContext della finestra, di modo che, a cascata, costituisca la sorgente
'dati per i controlli dipendenti come la ListView
Me.DataContext = (From proc In Process.GetProcesses).Select(Function(p) p)
End Sub
Ora proviamo ad avviare l’applicazione e a fare clic su uno dei pulsanti corrispondenti alle voci:
In questo modo riusciamo a gestire il Click di ogni pulsante sfruttando un singolo Routed event. Questa tecnica si può utilizzare anche con gli application commands, qui invece abbiamo visto anche come implementare un comando personalizzato.
Il progetto è scaricabile dall’area Download di Visual Basic Tips & Tricks a questo indirizzo.
Alessandro