11

I am building a composite application using CAL/Prism. The main region is a tab control, with multiple types of views in it. Each view has a custom set commands that it can handle which are bound to toolbar buttons at the top of the window. I've done this before in non-CAL apps by simply setting the InputBinding on the command, but I haven't been able to find any such mechanism in the source code for the CAL modules.

My question is, what is the best way to hook up a keystroke to my view, so that when the user presses Alt + T, the associated DelegateCommand object handles it? Hooking up a shortcut can't be THAT difficult...

Jasper
  • 2,166
  • 4
  • 30
  • 50
JMcDaniel
  • 358
  • 3
  • 12

2 Answers2

18

Just for reference, the CommandReference class is currently not included in an assembly that you can reference, but is included with the M-V-VM project template. So if you don't build your application from the template, then you have to get the class from somewhere else. I chose to copy it from the sample project. I included it below to allow everyone easy access to this little chunk of goodness, but be sure to check for updates to the template in future versions of the M-V-VM Toolkit.

/// <summary>
/// This class facilitates associating a key binding in XAML markup to a command
/// defined in a View Model by exposing a Command dependency property.
/// The class derives from Freezable to work around a limitation in WPF when data-binding from XAML.
/// </summary>
public class CommandReference : Freezable, ICommand
{
    public CommandReference( )
    {
    }
    public static readonly DependencyProperty CommandProperty = DependencyProperty.Register( "Command", typeof( ICommand ), typeof( CommandReference ), new PropertyMetadata( new PropertyChangedCallback( OnCommandChanged ) ) );

    public ICommand Command
    {
        get { return (ICommand)GetValue( CommandProperty ); }
        set { SetValue( CommandProperty, value ); }
    }

    #region ICommand Members

    public bool CanExecute(object parameter)
    {
        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;
        if (commandReference != null)
        {
            ICommand oldCommand = e.OldValue as ICommand;
            if (oldCommand != null)
                oldCommand.CanExecuteChanged -= commandReference.CanExecuteChanged;

            ICommand newCommand = e.NewValue as ICommand;
            if (newCommand != null)
                newCommand.CanExecuteChanged += commandReference.CanExecuteChanged;
        }
    }

    #endregion

    #region Freezable

    protected override Freezable CreateInstanceCore( )
    {
        return new CommandReference();
    }

    #endregion
}

Enjoy!

JMcDaniel
  • 358
  • 3
  • 12
  • 2
    Hey thanks, that'll be nice for the next guy that comes along. – Anderson Imes Sep 24 '09 at 14:11
  • Thanks for posting the code! Seems like I was "the next guy" that came along, looking for that code... :) – gehho Jul 07 '10 at 15:01
  • I can't find Freezable in Silverlight, what am I missing? – kenny Sep 16 '10 at 12:50
  • 1
    @ Kenny - This code is WPF specific. But check out the Silverlight MVVM Toolkit project on Codeplex. (http://silverlightmvvm.codeplex.com/) They should have an implementation of the necessary command objects. – JMcDaniel Sep 17 '10 at 20:52
14

The MVVM Toolkit has a class called a CommandReference that will allow you to use a reference to a command as a keybinding.

<Window ...
    xmlns:toolkit="clr-namespace:CannotRememberNamspace;assembly=OrTheAssembly"
    >

    <Window.Resources>
        <toolkit:CommandReference 
                 x:Key="ExitCommandReference" 
                 Command="{Binding ExitCommand}" />
    </Window.Resources>

    <Window.InputBindings>
        <KeyBinding Key="X" 
                    Modifiers="Control" 
                    Command="{StaticResource ExitCommandReference}" />
    </Window.InputBindings>
</Window>

This'll do it.

Edit: Since this was written, WPF 4.0 fixed this particular issue and you no longer have to use the static resource workaround. You can reference the command in your viewmodel directly from the KeyBinding.

Anthony
  • 12,177
  • 9
  • 69
  • 105
Anderson Imes
  • 25,500
  • 4
  • 67
  • 82
  • Perfect! It took me more time to find the CommandReference class than it did to hook it in. I literally had the key bindings working in two minutes. Thanks a ton. – JMcDaniel Sep 24 '09 at 13:42
  • I think both answers should be merged, the full answer is actually both (this and JMcDaniel's). One is imcomplete without the other. I needed both to solve the problem. Thanks! – bluediapente Jun 02 '11 at 13:39