EDIT 4: It seems this only happens when running the app through the debugger. So this is not a major problem.
I have a lot of custom keyboard controls, and a lot of it needs to fire regardless of what control has focus. So I'm using the following code in my MainPage constructor:
public MainPage()
{
Window.Current.CoreWindow.KeyDown += CoreWindow_KeyDown;
Window.Current.CoreWindow.KeyUp += CoreWindow_KeyUp;
}
public void public async void CoreWindow_KeyDown(CoreWindow sender, KeyEventArgs args)
{
// Handle here
}
But I'm having the worst kind of problem. Whenever things are happening on the UI thread it seems to be badly interfering with keyboard input. It's as if it stores some kind of backlog.
For example if I go to an AutoSuggestBox which does a lot of logic each keypress and populates the results with graphics loaded from a server and such, and I type 'abcd' it's quite often the 'd' won't register. Then when I type 'e' a few seconds later, the 'd' will go through but not the 'e'. It's not hardware related, it only does this in my UWP app I'm working on.
In the debugger I have confirmed that this unwanted behavior is all happening before the event is fired. args.VirtualKey is firing a 'd' when I type 'e'.
Also the keyup events only fire like 95% of the time. CoreWindow.PointerWheelChanged does not exhibit this problem. Gamepad input using the same handler as the keyboard does not have this problem either.
The more activity on the UI thread seems to increase the severity of the problem.
Does anyone know how to remedy this situation? Any kind of workaround or alternative solution, or at least an explanation of what might be happening?
EDIT:
I tried setting all 4 options for Window.Current.CoreWindow.Dispatcher.ProcessEvents(), no improvement.
EDIT 2:
It seems the fact that I'm capturing CoreWindow.Keydown for global events is a non-sequitur. The problem also happens with any regular KeyDown event on any focused control.
EDIT 3:
I believe I realize what's happening and I think it's a bug. My cursory understanding is that UWP keyboard input is sandboxed to prevent keylogger malware or something, so there's some lower-level translation between the raw key input and the VirtualKey that CoreWindow processes. That's fine, but it seems it doesn't work right under certain conditions.
When there is load on the UI thread during rapid keyboard input (like typing) it sometimes does not detect key releases. This is why KeyUp doesn't fire occasionally as I mentioned. This also messes up KeyDown because keys are in a lock state it thinks the key is being held down when in reality it is not. Then when the next key release does register, the CoreWindow dispatcher flushes its queue and the result is that it fires both an event for the previous key input as well as for the new one. So type 'abcd' and 'd doesn't fire. Wait 10 seconds and then press 'e'. Suddenly both 'd' and 'e' will appear. Or more likly press 'd' again because it didn't register the first time, and double 'dd' will display. Absolutely unacceptable behavior by any standard.
The best way you can try to reproduce it yourself is use an AutoSuggestBox that does something blocking like queries and loads image thumbnails in the results as you type. And keep in mind even a tiny bit of UI load seems to cause it. Even if I asynchronously preload the image as a stream or byte array, it still blocks the UI thread for 1-2ms when the BitmapImage source is set. 1-2ms less than a frame of video and thus visually imperceptible, but it seems it's enough to occasionally not detect when a keyboard key is released.
This could be something hardware specific. I've tested different keyboards but not a different computer.
You can try this:
public MainPage()
{
InitializeComponent();
Window.Current.CoreWindow.CharacterReceived += CoreWindow_CharacterReceived;
}
private async void CoreWindow_CharacterReceived(Windows.UI.Core.CoreWindow sender, Windows.UI.Core.CharacterReceivedEventArgs args)
{
if (args.KeyCode == 27 ) //Escape
{
// your code here fore Escape key
}
if (args.KeyCode == 13) //Enter
{
// your code here fore Enter key
}
}
You can use key codes for other keyboard characters.
Activity on the UI thread during rapid keyboard input prevents CoreWindow from detecting a key release, at least on some hardware, targeting 1803 Spring Creators Update. Hopefully someone knows a better solution but for now here is a partial workaround solution:
public void CoreWindow_KeyDown(Windows.UI.Core.CoreWindow sender, Windows.UI.Core.KeyEventArgs args)
{
CoreVirtualKeyStates keyState = sender.GetAsyncKeyState(args.VirtualKey);
if ((int)keyState == 3)
{
// KeyState is locked and pressed.
// Whenever this happens it means the event fired when I actaually pressed this key.
// Handle event.
}
else if ((int)keyState == 2)
{
// KeyState is locked but not pressed. How if it's not caps lock?
// When this happens it's an unwanted duplicate of the last keystroke.
// Do not handle event.
}
else if ((int)keyState == 0)
{
// Key state is None?!? How can a key that isn't currently down fire a KeyDown event?
// This is a phantom delayed rection of a missed event from two keystrokes ago.
// Do not handle event.
}
}
This will prevent the delayed reaction problem but there will still be missed keystrokes as a result. Not ideal but a vast improvement.
Here is the enum:
https://learn.microsoft.com/en-us/uwp/api/windows.ui.core.corevirtualkeystates
Oddly state 3 ("Pressed | Locked") that always represents a correct key input isn't in the documentation.
Also note that 'CoreVirtualKeyStates == 0' makes it clear this is a bug, at least with my hardware. How could a key with the 'None' state have fired a KeyDown event? Nobody's fingers are that fast. I think this is the CoreWindow dispatcher flushing its queue because it missed a KeyUp event.
Related
I was originally trying to get my program to get inputs of the arrow keys (Up, Down, Left and Right), but found out the hard way that in KeyDown(), those keys never made. Afterwards I found out that I could enable the arrow keys by going into the PreviewKeyDown() function and setting:
e.IsInputKey = true;
with whatever conditionals and logic around it. The trouble was that when I wrote the function:
private void Form1_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
{ /*whatever logic goes here*/}
it never fired; I even set a breakpoint that would trigger inside the function to be sure. Also, I tried:
this.Focus()
in the constructor to make sure that the main form had the focus, but it made no difference. The only thing that worked was setting the focus to a Button I had created and the button also trigger on a PreviewKeyDown event by calling the above Form1_PreviewKeyDown().
So at this point I have a working method, but can anyone help me understand why it never originally fired? I'm assuming that for some reason the Form's PreviewKeyEvent never fires, but I really have no idea why.
Why
You can try this little experiment: Make a form with two buttons, override PreviewKeyDown(), set a breakpoint, run it, and press the left/right arrow keys. The PreviewKeyDown() method won't be run. But delete the buttons and the override will be called.
The reason for the difference is that WinForms is handling the arrow keys itself for navigation. When you have input controls like buttons and text boxes, WinForms will automatically take over certain special keys like TAB and the arrow keys to navigate from one control to the next. It probably does this because a lot of people like to be able to use the keyboard to navigate, and it's easy to break that for them if you go messing with the navigation keys. Better to handle them for you so you don't mess them up by accident while you're playing with the other keys.
A naive workaround would be to detect when you form loses focus and take it back. This doesn't work though, because your form doesn't lose focus. The input controls have the focus, and they're part of the form, so the form still (technically, indirectly) has focus. It only loses the focus when you click outside on some other window.
A better workaround involves a better understanding of what's going on "under the covers", just below the .Net interpreter. WinForms mimics this level fairly closely, so it's a useful guide to understanding what WinForms is up to.
When Windows sends input (like keystrokes) to your program, your form isn't always the first to get the input. The input goes to whichever control has the focus. In this case, that control is one of the buttons (I'm assuming the focus glow is hidden at first to justify why nothing happens on the first stroke when nothing looks selected).
Once the button gets hold of the input, it gets to decide what happens next. It can pass the input on to whoever's next in line, do something and then pass it on, or completely handle the input and not pass it on at all.
With normal letter keys, the button decides it doesn't know what to do with them and passes them to its base class instead. The base class doesn't know either, so it forwards the key on. Eventually, it hits the Control class, which handles it by passing it on to whichever Control is in its Parent property. If that goes on long enough, your form will eventually get a chance to handle the input.
So in a nutshell, WinForms is giving the input to the most specific target first, then working out to more and more general things until someone knows how to handle the input.
In the case of the arrow keys, however, the button knows how to handle those. It handles them by passing the focus on to the next input control. At that point, the button declares the input totally handled, swallows the key and doesn't give anyone else a chance to look at it. Nobody after the button even knows the keystroke ever happened.
That's why your PreviewKeyDown() override isn't being called. It's only called when your Form gets a keystroke, but it never gets the keystroke because it went to an input control, the input control offered to let the navigation code look at it, and the navigation code swallowed it.
Workaround
Unfortunately, getting around this is going to be some work. The keystrokes are disappearing into the input controls, so you'll need to get all the input controls involved in getting the arrow keys into your form.
To do this, you'll need to derive new controls from all the input control types you use and use them in place of the originals. Then you'll have to override the OnPreviewKeyDown() method in each one and set e.IsInputKey = true. That'll get your arrow keys into the derived controls' KeyDown() handlers instead of having them stolen by the navigation code.
Next, you'll have to handle the KeyDown() event in all those controls, too. Since you want the arrow keys to raise events in the Form, all the derived controls will need to track down their form and pass the keys to that (which means the form's method will need to be public).
Putting all that together, the arrow-key-passing input controls will look about like this.
class MyButton : Button
{
public MyButton()
{
this.KeyDown += new KeyEventHandler(MyButton_KeyDown);
}
protected override void OnPreviewKeyDown(PreviewKeyDownEventArgs e)
{
e.IsInputKey = true;
base.OnPreviewKeyDown(e);
}
private void MyButton_KeyDown(object sender, KeyEventArgs e)
{
Form1 f = (Form1)this.FindForm();
f.Form1_KeyDown(sender, e);
}
}
That's going to be a bit error prone with all the repeated code.
An easier way would be to override your form's ProcessCmdKey() method and handle the keys there. Something like this would probably work:
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (keyData == Keys.Up || keyData == Keys.Down ||
keyData == Keys.Left || keyData == Keys.Right)
{
object sender = Control.FromHandle(msg.HWnd);
KeyEventArgs e = new KeyEventArgs(keyData);
Form1_KeyPress(sender, e);
return true;
}
return base.ProcessCmdKey(ref msg, keyData);
}
This effectively steals the command keys (those special navigation keys) even before the input controls get a chance at them. Unless those controls override PreviewKeyDown() and set e.IsInputKey = true. The child's PreviewKeyDown() method will come first, then the arrow will be considered not a command key and your ProcessCmdKey() won't be called.
ProcessCmdKey() is meant for context menu handling. I'm not sure whether it's wise to go using it for things other than context menus, but even Microsoft recommends it for similar kinds of use and it does seem to work, so it may be worth considering.
Conclusion
Long story short, navigation keys are meant for navigation. Messing with them can make the user experience unpleasant for keyboard users, so .Net makes it hard to get at them so you'll be encouraged to mess with other keys instead.
I had the same problem!
Luckily i found a dense answer :)
you can use the bool function in the definition of the Form class witch occurs on every key pressed. but remember to return the base function!
public partial class myForm : Form
{
public myForm ()
{
InitializeComponent();
}
protected override bool ProcessDialogKey(Keys keyData)
{
//Add your code here
return base.ProcessDialogKey(keyData);
}
}
hopefully i helped. but if my answer is incomplete please note me!
Keyboard events on the parent form are pretty useless unless you also set
this.KeyPreview = true;
see the MSDN documentation
I have this weird problem.
I made an app which has to react to the keyboard keys (1,2,3,4,...), but it doesn't.
Well, actually it does but only if I click a button and hold it clicked, if I let it go the keys, again, stop to react.
The method I used is KeyDown, it is placed in Page
KeyDown="Page_KeyDown" in XAML
and in Code:
private void Page_KeyDown(object sender, KeyRoutedEventArgs e)
{
switch (e.Key)
{
case Windows.System.VirtualKey.Number1:
{
KeyDownHelper(1);
break;
}
}
...
}
It has many cases but all of them are pretty the same, and I guess that the problem isn't inside the method but with my thinking.
I think the problem is that the page kind of loses focus maybe? But not sure if that is the case and how to deal with it
It sounds like you want your app to listen for individual key presses, rather just when they key is held down, is that right? In that case you should respond to the Key Up event. That way you will be able to react to each individual key press, and ignore times when the key is held down.
Here is a link to an MSDN article on Key Up: Key Up MSDN Article
Does that help?
I think you are looking for the CoreDispatcher.AcceleratorKeyActivated API.
For your reference CoreDispatcher.AcceleratorKeyActivated
I learned about Control.ModifierKeys in this thread and this thread, and FileSystemWatcher here and a FileSystemWatcher-specific workaround here. However, when I combine the two, I'm running into a strange, but explainable, bug. At least, I have a hypothesis.
My form uses a FileSystemWatcher to detect changes to a file and run something whenever it triggers. However, the thing it runs is a bit distracting and time wasting, so I'm trying to get it to skip the run if the save is done by Ctrl+S, i.e. when the Control key is held down.
The code is something like this:
private void onFileChanged(object source, FileSystemEventArgs e)
{
// The try/finally blocks prevent double raising of the changed event
try
{
myFileWatcher.EnableRaisingEvents = false;
if ((ModifierKeys & Keys.Control) != 0)
MessageBox.Show("Control held down!");
else
{
MessageBox.Show("Running stuff!");
}
}
finally
{
myFileWatcher.EnableRaisingEvents = true;
}
}
So that's all fine, noting that the two MessageBox.Show calls replace in-block code. Under normal use, it works. However, there is a very small time lag between the saving of the file and the triggering of onFileChanged. If the Ctrl key is released before onFileChanged triggers, i.e. if you tap Ctrl+S very quickly, very strange behavior results. For one, obviously, the Control key is not detected, but from here, even if you hold down Control after Ctrl+S until the popup shows, ModifierKeys will still be 0 and it will think Ctrl is not being held down. And then, if you hold down Ctrl while clicking the OK in the popup, all of a sudden ModifierKeys will keep firing the Control key even if you go File->Save in the file it's watching.
Is there a way to defeat this bug, or do I have to chalk it up as unfixable and tell my users to leave the Control key held down longer than they normally would in order to avoid this bug?
I would try to set a global keyboard hook and record if Ctrl+S is still pressed or has been pressed within a certain time before the filewatcher event is fired.
I am using this Global Keyboard Hook as found here: Using global keyboard hook (WH_KEYBOARD_LL) in WPF / C#
It works great, and I use a little piece of code to handle the keydown event.
private void Klistener_KeyDown(object sender, RawKeyEventArgs e)
{
if (e.Key == Key.Snapshot)
{
MessageBox.Show("Key Pressed!");
}
}
However, any other handlers tied to Key.Snapshot will still work. For instance Windows default for the key will print screen and save it into the clipboard, what if I didn't want any other actions happening after my handler?
I'm not entirely fluent in the Keyboard Hook I am using, but I'm sure there must be a way to implement a e.Handled property or something similar as can be found in KeyEventArgs.
Any ideas how I would go about doing this? Thanks.
The MSDN says :
If the hook procedure processed the message, it may return a nonzero value to prevent the system from passing the message to the rest of the hook chain or the target window procedure.
So you would need to just return 1 in LowLevelKeyboardProc if the desired key was pressed. Otherwise you return the call to CallNextHookEx
Are the two events the same or are there differences that we should take note when coding the keyboard presses?
My answer here is just based on experimenting with different applications, not programming per se.
Handle the keydown. Let that be the trigger for your logic. That's what the user would expect based on interacting with other applications.
For example, try a key down in Notepad, and you'll see that the DOWN triggers the reaction in Notepad. It doesn't wait for the UP.
It doesn't matter if it's .Net or not, it matters what the user expects. Keydown is a good time to respond to the four arrow keys. Character input is a good time to respond to input of visible characters. Keyup is usually a good time to respond to any action that is going to have an effect on a document, but keydown would be better if it's a game where the user wants immediate effect.
It's not really "which is a better choice for .NET."
You really have to think about how you want to respond to key presses. If you want to what someone is typing in a text box it's usually best to wait until they've released before trying to decide what they're doing. But if it's something like a game where you want to respond the instant it's pressed, than you would use KeyDown.
KeyDown is the moment the key is pushed down.
KeyUp is after the key has been released.
One thing to consider that I've had a problem with before:
If you handle something on key down that changes the window or changes UI component focus, then the new UI element may sometimes get the key up event.
This happened to me at my last job. We had a control on a form that, on key down, would load a new form. If the new form loaded fast enough, then the new form would get focus before the user released the key, and the new form would get the key up event. We had a UI control on that 2nd form that reacted to key up, and it would sometimes get triggered unintentionally.
The moral of the story is; keep it consistent. Pick one or the other and stick to it :)
The key down event happens as soon as the user presses a key, the key up event happens when they release the key after pressing it.
So if you use the key up event, and the user presses a key and holds it for 5 seconds, nothing will happen until they let go of it.
(Note: I know nothing about .net, I've just used 'key up' and 'key down' events in other libraries.)
I allmost allways use KeyDown, because then I can use e.Handled=True and stop the keyevent to travel from textbox to container and down in the eventque if I want. You can use e.Handled in KeyUp also, but then its "to late" because the key the user entered will be seen in the textbox and you have to take it away manually if you for example want to stop the user to enter digits in the textbox.
Another thing to take into account: When holding modifiers, it's important to use keydown. I generally use keydown to set a variable like ctrlPressed=true; then use keyup to unset that variable ctrlPressed=false;
I generally use keyPressed for all alphanumeric characters.
This sort of system allows you to enable things like CTRL+K+C ala Visual Studio