Alvaro Dias

Going the InputBindings way: Part 2 - .NET 3.5

In my previous post Going the InputBindings way I discussed ways to do input bindings in WPF.

Now, if you are on .NET 3.5, you will have noticed that the XAML I have provided does not work. Shown it below again for reference.

  <Window.InputBindings>
    <KeyBinding
      Gesture="CTRL+A"
      Command="{Binding KeyPressedCommand}" />
  </Window.InputBindings>

If you try setting the KeyBinding in XAML and bind the Command property and debug the application, you will get the following exception.

A first chance exception of type 'System.Windows.Markup.XamlParseException' occurred in PresentationFramework.dll

Additional information: A 'Binding' cannot be set on the 'Command' property of type 'KeyBinding'. A 'Binding' can only be set on a DependencyProperty of a DependencyObject.

This is because, prior to .NET Framework 3.0 and 3.5, the KeyBinding is not really part of the element tree, which means that it is not able to get the DataContext which in turn means that the KeyBinding.Command cannot really get the bound to KeyPressedCommand property.

We can get around this by creating a wrapper command which inherits from Freezable and we pass the our Command to it. Then, we can bind this command to our KeyBinding without any problem. Like so.

  <Window.Resources>
    <local:FreezableCommandWrapper
      x:Key="KeyPressedCommand"
      Command="{Binding KeyPressedCommand}" />
  </Window.Resources>
  <Window.InputBindings>
    <KeyBinding
      Gesture="CTRL+A"
      Command="{StaticResource KeyPressedCommand}" />
  </Window.InputBindings>

Here's a minimalistic implementation of the FreezableCommandWrapper class.

using System;
using System.Windows;
using System.Windows.Input;

namespace InputBindings
{
    class FreezableCommandWrapper : Freezable, ICommand
    {
        public static readonly DependencyProperty CommandProperty =
            DependencyProperty.Register("Command",
                typeof(ICommand),
                typeof(FreezableCommandWrapper),
                new UIPropertyMetadata());

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

        #region ICommand Members

        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }

        public bool CanExecute(object parameter)
        {
            return this.Command.CanExecute(parameter);
        }

        public void Execute(object parameter)
        {
            this.Command.Execute(parameter);
        }

        #endregion

        #region Freezable Members

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

        #endregion
    }
}

Till my next post.