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

14

48

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?

Phipps answered 7/12, 2008 at 16:7 Comment(0)
O
24

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.

Ofori answered 7/12, 2008 at 16:26 Comment(1)
Looks like the link above is not alive anymore.Brockway
R
61

Attach to the Window's Event

After the control is loaded, attach to the Window's KeyDown event (or any event) by using Window.GetWindow(this), like so:

The XAML

<UserControl Loaded="UserControl_Loaded">
</UserControl>

The Code Behind

private void UserControl_Loaded(object sender, RoutedEventArgs e) {
  var window = Window.GetWindow(this);
  window.KeyDown += HandleKeyPress;
}

private void HandleKeyPress(object sender, KeyEventArgs e) {
  //Do work
}
Rimbaud answered 15/2, 2013 at 2:40 Comment(2)
I need to double-check this, but the paranoid part of my brain says you should unbind the event when the control is unloaded. Else you'll retain a link from your UserControl to the Window object its hosting and they'll never be freed (memory-wise).Maddalena
I'd call this the answer. The marked answer is correct but this is more explicit about how to implement it.Anniceannie
C
29

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!

Cienfuegos answered 11/6, 2009 at 11:10 Comment(1)
Don't forget to set the focusable property on your loaded page/control when you try to focus it.Handsaw
O
24

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.

Ofori answered 7/12, 2008 at 16:26 Comment(1)
Looks like the link above is not alive anymore.Brockway
R
13

Setting the Focusable property to true on my UserControl solved the issue for me.

Restive answered 9/12, 2011 at 16:9 Comment(0)
W
12

I propose a method which strengthens the one @Doc mentioned.

The following code @Doc mentioned will work since the KeyDown routed event is bubbled to the outermost Window. Once Window receives the KeyDown event bubbled from an inner element, Window triggers any KeyDown event-handler registered to it, like this HandleKeyPress.

private void UserControl_Loaded(object sender, RoutedEventArgs e) {
  var window = Window.GetWindow(this);
  window.KeyDown += HandleKeyPress;
}

private void HandleKeyPress(object sender, KeyEventArgs e) {
  //Do work
}

But this += is risky, a programmer is more likely to forget to un-register the event-handler. Then memory leaking or some bugs will happen.

Here I suggest,

YourWindow.xaml.cs

protected override void OnKeyDown(KeyEventArgs e)
{
    base.OnKeyDown(e);

    // You need to have a reference to YourUserControlViewModel in the class.
    YourUserControlViewModel.CallKeyDown(e);

    // Or, if you don't like ViewModel, hold your user-control in the class then
    YourUserControl.CallKeyDown(e);
}

YourUserControlViewModel.cs or YourUserControl.xaml.cs

public void CallKeyDown(KeyEventArgs e) {
  //Do your work
}

There is no need to code in xaml.

Whiten answered 16/9, 2014 at 4:7 Comment(2)
I would be grateful if someone likes and upvotes my answer since the post isn't in a up-to-date subject and not easy to gain upvote.Whiten
If you are so conceited about your answer being the 'best' approach without leaks then consider checking logical focus so all controls that have similar solution won't respond to the event...Gibson
E
5

What exactly worked for me:

Only on window Loaded event listen to PreviewKeyDown event:

void GameScreen_Loaded(object sender, RoutedEventArgs e)
{
     this.PreviewKeyDown += GameScreen_PreviewKeyDown;
     this.Focusable = true;
     this.Focus();
}

void GameScreen_PreviewKeyDown(object sender, KeyEventArgs e)
{
     MessageBox.Show("it works!");   
}
Eloquent answered 9/3, 2014 at 13:13 Comment(0)
B
4

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.

Bridgman answered 25/8, 2019 at 18:9 Comment(1)
In modalless dialog, this code [ controlling Page_IsVisibleChanged() ] and PreviewKeyDown handler in XAML combination is helpful. Thanks.Ingressive
K
2

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.

Keverne answered 10/10, 2016 at 21:23 Comment(1)
This was my problem exactly. Thanks!Laris
T
2

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.

Tourmaline answered 26/8, 2019 at 11:14 Comment(0)
K
1

You can simply use the Window_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 Window_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>
Krill answered 24/7, 2019 at 11:6 Comment(0)
F
0

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

Ferdinandferdinanda answered 25/1, 2019 at 11:28 Comment(0)
R
0

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

Rapp answered 29/7, 2021 at 7:40 Comment(0)
H
0

Your UserControl has an initializer. Add the handler to that code behind. No need to do anything in XAML.

public partial class UserControl1 : UserControl
{
    public UserControl1()
    {
        InitializeComponent();
        KeyDown += Handle_KeyDown;
    }

    private void Handle_KeyDown(object sender, KeyEventArgs e)
    {
        throw new NotImplementedException();
    }
}

You don't need an onload event. That's included in the initializer. And keeping everything in one file will make maintaining it easier.

As others have mentioned, PreviewKeyDown might serve you better than KeyDown, depending on your needs.

Howardhowarth answered 26/2, 2023 at 17:39 Comment(0)
B
-1

This is tested and defintiely works.

  Private Sub textbox1_PreviewKeyDown(sender As Object, e As KeyEventArgs) Handles textbox1_input.PreviewKeyDown
        If (e.Key = Key.Down) Then
            MessageBox.Show("It works.")
        End If
    End Sub

'detect key state directly with something like this below
 Dim x As KeyStates = System.Windows.Input.Keyboard.GetKeyStates(Key.Down)

PreviewKeyDown is what most people miss. Think of PreviewKeyDown as an overall observer of keyboard events, (but cannot effect them if im not mistaken) and KeyDown evens are being listened to within the confines of the control or class you are currently in.

Brannon answered 3/1, 2016 at 0:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.