Il pattern Model-View-ViewModel è un interessante nuovo modo di organizzare e realizzare applicazioni in Windows Presentation Foundation e il suo punto di forza è dato dalla facilità con cui calza le caratteristiche di WPF senza costringere lo sviluppatore a costruire o utilizzare un framework di supporto.
Vi sono però delle eccezioni a cui bisogna porre rimedio; per esempio ogni UIElement dispone di una collezione InputBindings nella quale si possono inserire uno o più KeyBinding per associare un ICommand ad una precisa gesture della tastiera.
Purtroppo la proprietà Command non è una DependencyProperty e non è possibile effettuare quindi il binding sul sottostante ViewModel associato alla View corrente, a differenza di come si può invece fare con pulsanti, caselle di selezione, ecc.
Una possibile soluzione a questo problema consiste nel creare una classe che implementi ICommand e da istanziare all'interno della View come risorsa che faccia poi da tramite tra l'ICommand vero e il KeyBinding. In questo modo si può associare la risorsa tramite StaticResource al KeyBinding, mentre si può effettuare il Binding su tale risorsa legandola al ViewModel.
Ecco un esempio di utilizzo:
<Window x:Class="WpfApplication2.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:MyWpfApplication"> <Window.Resources> <local:CommandReference Command="{Binding SaveCommand}" x:Key="saveCommand" /> </Window.Resources> <Grid> <Grid.InputBindings> <!-- Shortcut Ctrl-S per salvare --> <KeyBinding Command="{StaticResource saveCommand}" Modifiers="Ctrl" Key="S" /> </Grid.InputBindings> <!-- Pulsante per salvare --> <Button Command="{Binding SaveCommand}">Salva</Button> </Grid> </Window>
La creazione della classe CommandReference è piuttosto semplice. Consiste in un classe DependencyObject che implementa l'interfaccia ICommand e con una proprietà Command la quale mappa i metodi Execute e CanExecute su quelli effettivi:
public class CommandReference : Freezable, ICommand
{
public static readonly DependencyProperty CommandProperty =
DependencyProperty.Register("Command", typeof(ICommand), typeof(CommandReference), new PropertyMetadata(new PropertyChangedCallback(OnCommandChanged)));
// Comando sottostante da usare
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
public bool CanExecute(object parameter)
{
// Redirige la richiesta sul commando sottostante
if (Command != null)
return Command.CanExecute(parameter);
return false;
}
public void Execute(object parameter)
{
Command.Execute(parameter);
}
public event EventHandler CanExecuteChanged;
private static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
CommandReference commandReference = d as CommandReference;
ICommand oldCommand = e.OldValue as ICommand;
ICommand newCommand = e.NewValue as ICommand;
// Mi aggancio per notificare quando il comando è eseguibile o meno
if (oldCommand != null)
{
oldCommand.CanExecuteChanged -= commandReference.CanExecuteChanged;
}
if (newCommand != null)
{
newCommand.CanExecuteChanged += commandReference.CanExecuteChanged;
}
}
protected override Freezable CreateInstanceCore()
{
throw new NotImplementedException();
}
}Commenti
Per inserire un commento, devi avere un account.
Fai il login e torna a questa pagina, oppure registrati alla nostra community.


