Reputation: 11243
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?
Upvotes: 50
Views: 96642
Reputation: 490
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>
Upvotes: 1
Reputation: 46
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.
Upvotes: 0
Reputation: 1910
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
Upvotes: 0
Reputation: 356
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.
Upvotes: 2
Reputation: 41
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.
Upvotes: 4
Reputation: 1
Try enabling your window with ElementHost.EnableModelessKeyboardInterop. In my case it capture arrow keys in the Keydown event.
Upvotes: 0
Reputation: 41
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.
Upvotes: 2
Reputation: 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.
Upvotes: -1
Reputation: 2271
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,
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);
}
public void CallKeyDown(KeyEventArgs e) {
//Do your work
}
There is no need to code in xaml.
Upvotes: 12
Reputation: 30418
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.
Upvotes: 24
Reputation: 8506
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!");
}
Upvotes: 5
Reputation: 11054
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
}
Upvotes: 62
Reputation: 3264
Setting the Focusable property to true on my UserControl solved the issue for me.
Upvotes: 13
Reputation:
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!
Upvotes: 29