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.