How can I capture KeyDown event on a WPF Page or UserControl object?

Viewed 84573

I have a Page with a UserControl on it. If the user presses Esc while anywhere on Page I want to handle.

I thought this would be as easy as hooking up the PreviewKeyDown event, testing for the Esc key, and then handling it. However, when I placed I breakpoint in the event handler I found it was never getting called. I thought perhaps the UserControl might be getting hit, so I tried PreviewKeyDown there... same result.

Does anyone know the proper place to test for a KeyDown or PreviewKeyDown on a Page object?

13 Answers

I have/had the same problem. I have a window which hosts a Frame which loads/navigates to various Page's which at their turn contain just 1 UserControl which contains normal Controls/Elements (Labels, Hyperlinks, etc).

what I discovered (after some hours of frustration of course!!!) is that if you do NOT have the focus on one of the Controls/Elements mentioned above the PreviewKeyDown event does NOT fire on the UserControl (stops somewhere at Frame level) but as soon as you put the focus (for instance calling ctrl.Focus() inside the UserCotrol's Loaded event handler) on one of the controls magic happens, it works, the event fires also for the UserControl.

of course thinking of it afterwards this makes enough sense but I am 100% sure this will cacth at least 5 out of 10 people by surprise :) crazy little thing called WPF...

cheers!

I believe that the PreviewKeyDown event is a tunneling routed event, rather than a bubbling one. If that is the case, then if your Page isn't getting the event, the UserControl shouldn't be either since it is below the Page in the visual tree. Maybe try handling it at the top level of your app (Window perhaps?) and see if it is getting the event?

Another option that might help would be to use something like Snoop in CodePlex to figure out where the events are going.

I had a similar issue when calling the WPF window out of WinForms. Neither KeyDown or PreviewKeyDown events were fired.

var wpfwindow = new ScreenBoardWPF.IzbiraProjekti();
    ElementHost.EnableModelessKeyboardInterop(wpfwindow);
    wpfwindow.Show();

However, showing window as a dialog, it worked

var wpfwindow = new ScreenBoardWPF.IzbiraProjekti();
    ElementHost.EnableModelessKeyboardInterop(wpfwindow);
    wpfwindow.ShowDialog();

PreviewKeyDown event fired like a charm for Escape and Arrow keys.

void MainWindow_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            switch (e.Key)
            {
                case Key.Escape:
                    this.Close();
                    break;
                case Key.Right:
                    page_forward();
                    break;
                case Key.Left:
                    page_backward();
                    break;
            }
        }

Hope this works.

If you don't want to attach to the window's event, add an event for visible changed of your UserControl or Page:

IsVisibleChanged="Page_IsVisibleChanged"

Then in code behind:

private void Page_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            if(this.Visibility == Visibility.Visible)
            {
                this.Focusable = true;
                this.Focus();
            }
        }

Now your event KeyDown would be fired if you press any key.

You can simply use the Weindow_KeyDown event.

here is an example

private void Window_KeyDown(object sender, KeyEventArgs e)
        {
            // ... Test for F1 key.
            if (e.Key == Key.F1)
            {
                this.Title = "You pressed F1 key";
            }
        }

don't forget to add the Weindow_KeyDown attribute to the Window tag

<Window x:Class="WpfApplication25.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525"
        KeyDown="Window_KeyDown">
</Window>

An alternative to the solution of @Daniel, that doesn't require you to add an event handler for the entire Window, is to do the following:

public partial class MyControl : UserControl{

public MyControl()
{
   MouseEnter+= MouseEnterHandler;
   MouseLeave+= MouseLeaveHandler;
}

protected void MouseEnterHandler(object sender, MouseEventArgs e)
{
   var view = sender as MyControl;
   view.KeyDown += HandleKeyPress;
   view.KeyUp += HandleKeyReleased;
   view.Focus();
}

protected void MouseLeaveHandler(object sender, MouseEventArgs e)
{
   var view = sender as MyControl;
   view.KeyDown -= HandleKeyPress;
   view.KeyUp -= HandleKeyReleased;
}

protected void HandleKeyPress(object sender, KeyEventArgs e)
{
    // What happens on key pressed
}

protected void HandleKeyReleased(object sender, KeyEventArgs e)
{
    // What happens on key released
}

}

This way you can have different handles of the same event in different parts of the view. The handle is local and specific to the control, you don't add handles to the global window.

Try enabling your window with ElementHost.EnableModelessKeyboardInterop. In my case it capture arrow keys in the Keydown event.

I fixed this issue just calling .Focus() on a button in the view and make sure the the window has finished the loading of all elements

Related