In Winforms, PreviewKeyDown() never fired for ANY key - c#

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

Related

TextBox handle key events and don't pass them on

I have a uwp app with some global keypress handling. I do it by subscribing to two different sets of events:
CoreWindow.CharacterReceived (for text keypresses), and
MainPage.KeyDown (for escape, arrow keys, enter, etc.)
But my app also has some TextBoxes. When one of those has focus, I want to disable the above in almost all cases. (arrow keys and tab are sometimes exceptions).
I could certainly do that by overriding OnGotFocus and OnLostFocus in my TextBox wrapper objects and keeping track of whether or not a TextBox currently has focus.
Is there a better way?
EDIT: I've found FocusManager.GetFocusedElement(). This is an improvement, but still does not feel ideal.
Maybe you can use the FocusManager.GetFocusedElement() and check whether the returned element is of type TextBox. So you should add something like this to your CoreWindow.CharacterReceived and MainPage.KeyDown event handlers:
EventHandler(parameters)
{
if (FocusManager.GetFocusedElement() is TextBox)
{
return;
}
// event handling
}

KeyDown not firing for Up, Down, Left and Right

On a form I have a panel with some buttons. When button1 is clicked I replace the panel with a new UserControl which has a label (e.g. this.Controls.Clear(), this.Controls.Add(UserControl1)). Except the label on my userControl has a KeyDown handler. It works fine, the event fires, but not for keys Up, Down, Left and Right. Can anybody explain why there is a difference between these keys? What decides whether the event is fired or not?
Two basic reasons. First the mysterious one: a Label control cannot receive the focus so can't see keystrokes. The reason its KeyDown event is hidden in the designer. Not so sure why you see any keystrokes at all. The more common reason is that the cursor and TAB keys are used for navigation, moving the focus from one control to another. Which is done before the key is passed to the control. You'd have to override the control so you can override its IsInputKey() method. But more practically you'd override the UserControl's ProcessCmdKey() instead to solve both issues.
Also note that you've got a nasty handle leak in your program. Never call Controls.Clear() without also calling the Dispose() method on the controls you remove. Unless you intended to reuse them later, not so common. It is a nasty kind of leak that the garbage collector doesn't solve and ultimately crashes your program after first making it slow and unwieldy.
According to MSDN:
This event supports the .NET Framework infrastructure and is not intended to be used directly from your code.
Occurs when the user presses a key while the label has focus.
Edit: There doesn't seem to be an alternative event for this. From what I've read, arrows keys should definitely be detected. Please supply some code.
You should override method ProcessCmdKey instead. Arrow keys are not processed the same way as other standard keys. Another solution would be this one: http://msdn.microsoft.com/en-us/library/system.windows.forms.control.previewkeydown.aspx suggested by Microsoft.
To handle arrow keys you may either set the Form's KeyPreview property to true and then handle them at form level instead specific control level. I have done so and it works perfect!
In case, above does not work for you then consider ProcessCmdKey something like this:
protected override bool ProcessCmdKey(ref System.Windows.Forms.Message msg, System.Windows.Forms.Keys keyData)
{
if(KeyData == Keys.Right)
{
//Move Right
return true;
}
else
{
return base.ProcessCmdKey(msg, keyData);
}
}

Focus picturebox when entering form

I am creating a simple game for school in C#, where I am controlling a character using the WASD keys. The character is taken from a sprite sheet and put into an imagelist. The imagelist is in a picturebox.
Everything works fine when it's just the picturebox in the form, but when I add a button or something else, it's like it lose focus. It doesn't respond.
I have searched endless pages for a solution to set focus on the picturebox when the form opens, but I haven't found anything that works.
I would really appreciate some help.
Edit: It's WinForms.
The PictureBox cannot take the focus. It is intended as a way to show an image but not intended to allow user input such as via the keyboard.
A crude approach would be to intercept the OnKeyDown event on the Form itself and then test for the keys of interest. This will work as long as the control that has the focus, such as your Button, does not want to process those keys itself.
A better approach would be to override ProcessCmdKey() method of the Form. This method is called on the target control, such as your Button, to decide if the key is special. If the Button does not recognize it as special then it calls the parent control. In this way your Form level method will be called for each key press that is not a special key for the actual target. This allows the Button to still process a ENTER key which is used to press the Button but other keys will be processed by your Form.
Lastly, to intercept all keys before they are handled by any of the controls on the Form you would need to implement the IMessageFilter interface. Something like this...
public partial class MyWindow : Form, IMessageFilter
{
public MyWindow()
{
InitializeComponent();
Application.AddMessageFilter(this);
}
public bool PreFilterMessage(ref Message m)
{
// WM_KEYDOWN
if (m.Msg == 0x0100)
{
// Extract the keys being pressed
Keys keys = ((Keys)((int)m.WParam.ToInt64()));
// Test for the A key....
if (keys == Keys.A)
{
return true; // Prevent message reaching destination
}
}
}
return false;
}
I found event MouseHover with pictureBox1_Hover calling pictureBox1.Focus() worked. When the mouse was hovered over the PictureBox in question, it would gain focus. Other than that, it didn't seem that calling pictureBox1.Focus() during form load had any effect on the focus.
this.pictureBox1.MouseHover += new System.EventHandler(this.pictureBox1_Hover);
private void pictureBox1_Hover(object sender, EventArgs e)
{
pictureBox1.Focus();
}
It worked for me!

How do I prevent a tab from rendering when selected?

I would like to be able to have a user be able run through the tabs, setting focus to each one, but only when they hit enter, the tabpage will render.
You would think that the paint event would be involved, but I don't know how to "cancel out" of it, if that would even do the job..
First, I should caution you that you're overriding the standard Windows behavior. In any property page dialog or anywhere else that uses tabs in the user interface, using the left and right arrow keys will flip through the tabs and cause them to display their contents in the tab control. You do not have to press Enter to get the selected tab page to display. Make sure that your users understand that your application is different (and that you understand the needs of your users) if you decide to go this route.
That said, you can override this behavior by handling the KeyDown event for the TabControl, detecting when one of the arrow keys has been pressed, and cancelling it. For example:
private void myTabControl_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
{
//Check to see if an arrow key was pressed
if ((e.KeyCode == Keys.Left) || (e.KeyCode == Keys.Right))
{
//Cancel the keypress by indicating it was handled
e.Handled = true;
}
}
However, once you do this, there will be no way for the user to set focus to a particular tab page's tab, because once the tab gets focus, the tab page is immediately brought into view. This is handled by the parent TabControl and is unrelated to the Paint event (which is responsible for how the control gets painted, not when or why).
Of course, you can always determine if the Enter key was pressed in the same KeyDown event and activate any tab page that you wish (such as by using a counter variable that is incremented/ decremented each time the corresponding arrow key is pressed), but there will be no visible indication to the user which tab will then be brought into view. The focus rectangle will not be drawn.
Also be aware that pressing Ctrl+Tab or Ctrl+Page Up/Page Down will switch between tab pages. If this is also undesirable, you'll need to watch for and cancel these key combinations as well.Any time you start trying to override default behaviors, you're in for a lot more trouble than if you just design your application around it. If there's a particular reason you want to require the Enter key to commit tab page switching, we might be able to help you come up with an easier and better solution.
I'm not sure I understand what you are trying to accomplish, but it sounds like you can do it using the Visible property.
You should be able to set the TabPage's visibility to false when the user switches to it, and then set it to true only when you want to.

C#: Activate a TextBox when user starts typing

I want to activate a search textbox when the user starts to type something (even if the textbox isnt focused right then). I have come as far as setting KeyPreview on the form to true. Then in the KeyDown event handler, I have this:
if(!searchTextBox.Focused)
{
searchTextBox.Focus();
}
This almost works. The textbox is focused, but the first typed letter is lost. I guess this is because the textbox never really gets the event, since it wasn't focused when it happend. So, do anyone have a clever solution to how I could make this work like it should?
I would also like some tips to how make this only happen when regular keys are pressed. So not like, arrow keys, modifier keys, function keys, etc. But this I will probably figure out a way to do. The previous issue on the other hand, I am not so sure how I should tackle...
As an addition to the answer from SDX2000, have a look at the MSDN article for the KeyDown event. It also refers to the KeyPressed event, which is fired after the KeyDown event, I think it is better suited for what you want to do.
Quoting MSDN:
A KeyPressEventArgs specifies the character that is composed when the user presses a key. For example, when the user presses SHIFT + K, the KeyChar property returns an uppercase K.
private void YourEventHandler(object Sender, KeyPressEventArgs Args)
{
if(!searchTextBox.Focused)
{
searchTextBox.Focus();
searchTextBox.Text += Args.KeyChar;
// move caret to end of text because Focus() selects all the text
searchTextBox.SelectionStart = searchTextBox.Text.Length
}
}
I'm actually not certain that you can use += to append a char to a string, so you need to check on that. And I don't know what happens when the user hits return, better follow up an that issue as well.
You will get the first key stroke as part of the KeyDown event. You can save it to the textbox yourself.

Categories