Utilizzare il KeyBinding di WPF con il pattern M-V-VM

di Cristian Civera, in Windows Presentation Foundation,

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

Visualizza/aggiungi commenti

| Condividi su: Twitter, Facebook, LinkedIn

Per inserire un commento, devi avere un account.

Fai il login e torna a questa pagina, oppure registrati alla nostra community.

Approfondimenti

Nessuna risorsa collegata

I più letti di oggi