di: Andrea Boschin 19 Maggio 2010
Un'applicazione in cui sia possibile gestire esclusivamente il Click di pulsanti ha sicuramente vita breve. Ciononostante, la attuale versione di Prism mette a disposizione solamente questa implementazione, perciò se come spesso capita dobbiamo gestire qualcosa di più complesso ecco che è necessario scrivere un po' di codice aggiuntivo. Il codice che vedremo è abbastanza complesso, tuttavia una volta che si è preparato lo snippet di codice è sufficiente copiarlo con poche modifiche e i nuovi comandi nasceranno in pochi istanti.
Supponiamo perciò di voler aggiungere una nuova feature al nostro esempio. Mediante una ComboBox è possibile selezionare una categoria di ricerca per filtrare con più efficacia i prodotti. La selezione di questa ComboBox tuttavia deve applicare il filtro immediatamente, senza attendere una ulteriore pressione del pulsante Search. Questo comportamento normalmente lo si ottiene gestendo l'evento SelectionChanged ma in un'ottica MVVM dovremo scrivere un apposito comando.
La prima cosa da fare è creare una classe estendendo CommandBehaviorBase<T>. Il tipo generico indica il controllo a cui si deve applicare il behavior. Nel nostro caso potrebbe essere riferito alla ComboBox ma dato che l'evento SelectionChanged prende origine dal controllo primitivo Selector possiamo rendere il comando più ampiamente utilizzabile se lo riferiamo a quest'ultimo.
public class SelectionChangedCommandBehavior : CommandBehaviorBase<Selector>
{
public SelectionChangedCommandBehavior(Selector targetObject) : base(targetObject)
{
targetObject.SelectionChanged += (s, e) => base.ExecuteCommand();
}
}
Lo scopo di questa classe è evidente. Essa è incaricata di agganciare il giusto evento e di notificare l'esecuzione del comando. Versioni più complesse potrebbero ad esempio valorizzare il CommandParameter con un valore preso dal controllo di riferimento. L'unico limite è dato dalla fantasia di chi scrive il codice. Aggiungendo questo metodo, ad esempio, possiamo fare in modo che il controllo sia nascosto quando il comando non può essere eseguito, anziché disabilitato:
protected override void UpdateEnabledState()
{
if(this.Command.CanExecute(this.CommandParameter))
this.TargetObject.Visibility = Visibility.Visible;
else
this.TargetObject.Visibility = Visibility.Collapsed;
}
A questo punto siamo pronti per scrivere tre attached properties. Le prime due sono le ovvie Command e CommandParameter che hanno lo scopo di valorizzare questi parametri per mezzo di XAML. La terza proprietà, che sarà invisibile a livello di markup, ha il solo scopo di memorizzare l'istanza di behavior creata per questo elemento. Creiamo le proprietà:
public static class SelectionChanged
{
public static readonly DependencyProperty SelectionChangedBehaviorProperty =
DependencyProperty.RegisterAttached("SelectionChangedBehaviorProperty",
typeof(SelectionChangedCommandBehavior),
typeof(SelectionChangedCommandBehavior),
null);
#region CommandProperty
public static readonly DependencyProperty CommandProperty =
DependencyProperty.RegisterAttached("Command", typeof(ICommand),
typeof(SelectionChanged),
new PropertyMetadata(CommandProperty_Changed));
public static ICommand GetCommand(DependencyObject obj)
{
return (ICommand)obj.GetValue(CommandProperty);
}
public static void SetCommand(DependencyObject obj, ICommand value)
{
obj.SetValue(CommandProperty, value);
}
private static void CommandProperty_Changed(DependencyObject dependencyObject,
DependencyPropertyChangedEventArgs e)
{
Selector targetObject = dependencyObject as Selector;
if (targetObject != null)
GetOrCreateBehavior(targetObject).Command = e.NewValue as ICommand;
}
#endregion
#region CommandParameterProperty
public static readonly DependencyProperty CommandParameterProperty =
DependencyProperty.RegisterAttached("CommandParameter", typeof(object),
typeof(SelectionChanged),
new PropertyMetadata(CommandParameterProperty_Changed));
public static ICommand GetCommandParameter(DependencyObject obj)
{
return (ICommand)obj.GetValue(CommandParameterProperty);
}
public static void SetCommandParameter(DependencyObject obj, ICommand value)
{
obj.SetValue(CommandParameterProperty, value);
}
private static void CommandParameterProperty_Changed(DependencyObject dependencyObject,
DependencyPropertyChangedEventArgs e)
{
Selector targetObject = dependencyObject as Selector;
if (targetObject != null)
GetOrCreateBehavior(targetObject).CommandParameter = e.NewValue;
}
#endregion
private static SelectionChangedCommandBehavior GetOrCreateBehavior(Selector targetObject)
{
SelectionChangedCommandBehavior behavior =
targetObject.GetValue(SelectionChangedBehaviorProperty) as
SelectionChangedCommandBehavior;
if (behavior == null)
{
behavior = new SelectionChangedCommandBehavior(targetObject);
targetObject.SetValue(SelectionChangedBehaviorProperty, behavior);
}
return behavior;
}
}
Il metodo GetOrCreateBehavior, riportato verso la fine della classe, verrò chiamato ogni volta le proprietà Command e CommandParameter sono valorizzate. In questo modo la prima volta il metodo crea una istanza del behavior e di conseguenza aggancia gli eventi del controllo.
Le volte successive l'istanza del behavior viene recuperata dalla apposita attached property dove è stato memorizzato precedentemente. In questo modo, quanto l'evento SelectionChanged viene sollevato, il relativo comando viene eseguito. Ora non resta che inserire il comando nel markup e scrivere il codice che lo gestisca:
<ComboBox Grid.Row="1" Grid.ColumnSpan="3" Margin="3,0,3,3"
ItemsSource="{Binding Categories}"
code:SelectionChanged.Command="{Binding CategoryChangedCommand}"
SelectedItem="{Binding SelectedCategory, Mode=TwoWay}"
DisplayMemberPath="Name" />
Il comando apparirà a tutti gli effetti come quello di Click dei pulsanti, anche se ovviamente nel namespace che rappresenta il nostro codice. Lascio al codice allegato a questo articolo la parte di implementazione del ViewModel che a questo punto dovrebbe essere abbastanza chiara.
Come dicevo in precedenza il codice da scrivere per ogni comando è molto e soprattutto ripetitivo. Per questo motivo è un ottimo candidato per uno snippet di VisualStudio. Nel link riportato in [1] potrete trovare lo snippet già confezionato, pronto da essere importato in Visual Studio.
Guida Windows Azure Code SnippetsLe migliori pratiche per far girare le applicazioni "in the cloud",... |
Guida ASP.NET MVC Best PracticesUn workflow dettagliato e ricco di suggerimenti pratici per... |
Guida ASP.NET Starter KitUn modo semplice per imparare ad utilizzare le tecnologie Microsoft... |
Ogni giovedì, direttamente nella tua e-mail: articoli, guide, tutorial e script ASP, ASP.Net, SQL server e IIS.
Iscriviti alla newsletter
|
|
Corso Progettazione database11 Maggio 2012 a Milano |
|
|
Amministratore di Reti Windows Server 200811 Giugno 2012 a Milano |
|
Nessun corso previsto |