Search code examples
c#wpfmenucommandbindinginputbinding

InputBinding and WebBrowser control


I have a very simple application where I'm trying to bind keyboard shortcuts to a WPF command that is bound to a menu item. The application itself consists of just a Menu and a WebBrowser control.

When I'm within the WebBrowser, the keyboard shortcuts are not routed up to the WPF Menu. For example, typing 'Ctrl+O' when focused in the web browser shows the IE open page. Additionally, in this application, unless I have the Menu focused (by typing in Alt) the input bindings don't fire. For example, I can't focus on the WPF window by clicking on the title bar and then type the shortcuts. The full code is replicated below:

MainWindow.xaml

<Window x:Class="TestInputBindingsOnMenu.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="600" Width="800">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <Menu IsMainMenu="True" x:Name="_mainMenu" Grid.Row="0" />
        <WebBrowser Source="http://google.com" Grid.Row="1" />
    </Grid>
</Window>

MainWindow.xaml.cs

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

namespace TestInputBindingsOnMenu
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            Initialize();
        }

        private void Initialize()
        {
            MenuItem fileMenu = new MenuItem();
            MenuItem fileNew = new MenuItem();
            MenuItem fileOpen = new MenuItem();
            MenuItem fileExit = new MenuItem();

            fileMenu.Header = "File";
            fileNew.Header = "New";
            fileOpen.Header = "Open";
            fileExit.Header = "Exit";

            fileMenu.Items.Add(fileNew);
            fileMenu.Items.Add(fileOpen);
            fileMenu.Items.Add(fileExit);

            _mainMenu.Items.Add(fileMenu);

            var fileNewCommand = CreateCommand("New");
            var fileOpenCommand = CreateCommand("Open");
            var fileExitCommand = CreateCommand("Exit");

            _mainMenu.CommandBindings.Add(new CommandBinding(fileNewCommand, ExecuteNew));
            _mainMenu.CommandBindings.Add(new CommandBinding(fileOpenCommand, ExecuteOpen));
            _mainMenu.CommandBindings.Add(new CommandBinding(fileExitCommand, ExecuteExit));

            fileNew.Command = fileNewCommand;
            fileOpen.Command = fileOpenCommand;
            fileExit.Command = fileExitCommand;

            _mainMenu.InputBindings.Add(new InputBinding(fileNewCommand, new KeyGesture(Key.N, ModifierKeys.Control)));
            _mainMenu.InputBindings.Add(new InputBinding(fileOpenCommand, new KeyGesture(Key.O, ModifierKeys.Control)));
            _mainMenu.InputBindings.Add(new InputBinding(fileExitCommand, new KeyGesture(Key.F4, ModifierKeys.Alt)));
        }

        private void ExecuteNew(object sender, ExecutedRoutedEventArgs e)
        {
            MessageBox.Show("New!!");
        }

        private void ExecuteOpen(object sender, ExecutedRoutedEventArgs e)
        {
            MessageBox.Show("Open!!");
        }

        private void ExecuteExit(object sender, ExecutedRoutedEventArgs e)
        {
            MessageBox.Show("Exit!!");
        }

        private static RoutedCommand CreateCommand(string label)
        {
            return new RoutedCommand(label, typeof(MainWindow));
        }
    }
}

Solution

  • Easy Solution

    Add the input bindings to the WebBrowser control as well as the main menu:

    Browser.InputBindings.Add(new KeyBinding(ApplicationCommands.Open, 
    new KeyGesture(Key.O, ModifierKeys.Control)));
    

    Hard Solution

    What is happening here is that the UIElement is using the KeyDown event, while you want to use PreviewKeyDown event, or add a handler that also handles Handled routed events. Look up Tunnelling and Bubbling if you don't know about this.

    Since this is handled in the UIElement class, I would advise using a different pattern in this situation. The MVVM Light framework provides an EventToCommand behavior. If you can route the PreviewKeyDown event of the window to the right commands instead of using KeyBinding with the InputBindings collection of the UIElement you will have your solution.

    You will need some custom code to check which key was pressed and to which command the routing should be.