How can I bind key gestures in Caliburn.Micro?

Joe White picture Joe White · Nov 15, 2010 · Viewed 7.9k times · Source

How can I get Caliburn.Micro to map a key gesture to an action method on my ViewModel?

For example, I want to implement a tabbed interface, and I want my ShellViewModel to have a NewTab method, which the user should to be able to invoke by pressing Ctrl+T on the keyboard.

I know that the full Caliburn framework has support for gestures, but how can I do this using Caliburn.Micro? Is there perhaps some way to bind an action to a RoutedCommand (since RoutedCommands already support input gestures)? Or some other way to get gesture support?

Answer

Gregor Slavec picture Gregor Slavec · Nov 23, 2011

I modified example to enable support for global key-bindings. You just need to add the folowing code to your view:

<i:Interaction.Triggers>
        <common:InputBindingTrigger>
            <common:InputBindingTrigger.InputBinding>
                <KeyBinding Modifiers="Control" Key="D"/>
            </common:InputBindingTrigger.InputBinding>
            <cl:ActionMessage MethodName="DoTheMagic"/>
        </common:InputBindingTrigger>
    </i:Interaction.Triggers>

And whenever Ctr+D is pressed the method DoTheMagic will be exexuted. Here is the modified InputBindingTrigger code:

public class InputBindingTrigger : TriggerBase<FrameworkElement>, ICommand
  {
    public static readonly DependencyProperty InputBindingProperty =
      DependencyProperty.Register("InputBinding", typeof (InputBinding)
        , typeof (InputBindingTrigger)
        , new UIPropertyMetadata(null));

    public InputBinding InputBinding
    {
      get { return (InputBinding) GetValue(InputBindingProperty); }
      set { SetValue(InputBindingProperty, value); }
    }

    public event EventHandler CanExecuteChanged = delegate { };

    public bool CanExecute(object parameter)
    {
      // action is anyway blocked by Caliburn at the invoke level
      return true;
    }

    public void Execute(object parameter)
    {
      InvokeActions(parameter);
    }

    protected override void OnAttached()
    {
      if (InputBinding != null)
      {
        InputBinding.Command = this;        
        AssociatedObject.Loaded += delegate {
          var window = GetWindow(AssociatedObject);
          window.InputBindings.Add(InputBinding);
        };
      }
      base.OnAttached();
    }

    private Window GetWindow(FrameworkElement frameworkElement)
    {
      if (frameworkElement is Window)
        return frameworkElement as Window;

      var parent = frameworkElement.Parent as FrameworkElement;      
      Debug.Assert(parent != null);

      return GetWindow(parent);
    }
  }