Userform ArrowKey KeyPreview Issues

XL Jedi

Active member
Joined
Apr 2, 2007
Messages
44
Location
Florida
Programming Experience
10+
This one's been an annoying thorn in my side...

I've developed a graphics based application on a userform with the GDI. I've set KeyPreview to True so I can add shortcut hotkeys to my app on the keydown event. So far so good...

I've built a "Nudge" tool that moves selected graphic shapes one pixel if the appropriate arrow key is pressed.

Even though I have KeyPreview set to true I'm having 2 problems with the arrow key nudge.

1) If I don't have a textbox on the userform, the arrowkeys are not detected regardless of the KeyPreview set to True.

2) Now that I do have a half-dozen or so textboxes on my userform, the arrowkeys are registering with the form as if I want to use them to advance between the textboxes. After 3 or 4 keypresses it seems to "break-thru" and my intended Nudge starts to work.


At the moment, I'm most interested in overcoming item number 2 above, as I hate the fact that I have to press the arrowkey repeatedly before my nudget is recognized. However, I'd also like to know the answer to item 1 because I may very well want to use the arrowkeys on a form in the future that does not contain textboxes.

Here's a snippet showing my arrow key assignments for the userform keydown event:
VB.NET:
Private Sub Frm_MoBo_KeyDown(ByVal sender As System.Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles MyBase.KeyDown
        ' Sets Handled to true to prevent other controls from 
        ' receiving the key if an arrow key was pressed
        Select Case e.KeyCode
            Case Keys.Right, Keys.Left, Keys.Up, Keys.Down
                TxtArrowKeyEnable.Select()
                Nudge(e.KeyCode)
                e.Handled = True

I thought by setting e.Handled to true I was prohibiting the userform textboxes from receiving the arrowkey?
 
Last edited:
could you post the code for the Nudge routine? the code currently posted looks correct

VB.NET:
Private Sub Nudge(ByVal key As Integer)
        If Not ActiveUnit Is Nothing Then
            Dim x As Integer
            Dim y As Integer
            Select Case key
                Case Keys.Right : x = 1
                Case Keys.Left : x = -1
                Case Keys.Up : y = -1
                Case Keys.Down : y = 1
            End Select
        End If

        'Move all units in the Relative_Unit collection 
        Dim Unit As Cls_Unit
        For Each Unit In Relative_Units()
            Unit.Origin.X = Unit.Origin.X + x
            Unit.Origin.Y = Unit.Origin.Y + y
        Next

        Draw_Units()

    End Sub

Nothing remarkable about the Nudge; just a select case that redraws an image with an appropriate (x,y) single pixel offset dependent on which key was pressed. ...and there's no point diggin beyond to look at my Unit class structures, collections, and code for drawing the collection. The problem isn't in there...

I might've thought there was a keyup that needed to be disabled if it wasn't for the fact that the arrowkey activates the stupid textboxes as soon as you press down on an arrowkey.

Have you ever tried just coding a msgbox popup for an arrowkey keydown event on a userform that does not have a textbox present? If I could just get something like that to work I could probably sort it out.
 
I tried re-writing the code for ya which has a slightly different approach, I doubt it's the fix but it's worth a try:
VB.NET:
Private Sub Nudge(ByVal key As Integer)
  'If Not ActiveUnit Is Nothing Then
    For Each Unit As Cls_Unit In Relative_Units()
      Select Case key
        Case Keys.Right : Unit.Origin.X += 1
        Case Keys.Left : Unit.Origin.X -= 1
        Case Keys.Up : Unit.Origin.Y -= 1
        Case Keys.Down : Unit.Origin.Y += 1
      End Select
    Next Unit
  'End If
  Draw_Units()
End Sub
 
Thanks... but that code is fundamentally no different; and as I mentioned, it has no bearing on the problem.

Just try this:

* Open a new project
* Add a userform
* Set userform KeyPreview property to True
* Add a textbox to the userform
* Set textbox tabstop to False
* Add this code:

VB.NET:
Public Class Form1

    Private Sub Form1_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles Me.KeyDown
        Select Case e.KeyCode
            Case Keys.Right, Keys.Left, Keys.Up, Keys.Down
                MsgBox(e.KeyCode)
                e.Handled = True
        End Select
    End Sub

End Class

Makes no sense to me why the ArrowKeys must activate a non-tabstopped textbox before shifting to its KeyDown assignment. :(

If the textbox doesn't exist on the userform, no problems. If the textbox is allowed to be a tabstop and is active, no problems.

I thought the whole purpose of setting tabstop to false was so arrowkey and tab activity would NOT select the textbox! ...and here it does. :confused:
 
I just made a quick project that has an assortment of controls (one being a textbox) and I tested the form's KeyDown and KeyUp events simply moving all the controls Up, Down, Left or Right and everything worked fine. it's in VB 2003 but it'll convert to VB 2005 without needing any code changes.
 
I just made a quick project that has an assortment of controls (one being a textbox) and I tested the form's KeyDown and KeyUp events simply moving all the controls Up, Down, Left or Right and everything worked fine. it's in VB 2003 but it'll convert to VB 2005 without needing any code changes.

If I run that in 2005 it's a disaster...

The arrowkeys first keypress activates the button, then it moves to the textbox, then it registers with the keydown and moves the controls, but while it's moving the controls there's also an editing I-Bar inside the textbox that's registering the arrowkeys and moving around.

If I add to your code...
e.Handled = True

It gets only slightly better, not moving around the I-Bar, but the blasted thing still selects the controls before registering with the keydown. I've gotta find a way to specify that the controls are to ignore the arrowkeys, unless the controls are activated by the user. I just can't have the arrowkeys bouncin round the form...
 
Last edited:
KeyPreview only works for InputKeys. Certain keys (such as the arrow keys, Tab, Enter, ...) are considered "special" keys and require preprocessing. Normally, these keys are used to navigate through the controls on a form that can receive focus (arrow and Tab keys) or to perform the default method of the control (Ex.: the Enter key will perform the click event of a button).
All classes based on the System.Windows.Forms.Control have a Protected Overridable method named IsInputKey which returns true if the specified key is a regular input key; otherwise false. If it returns false, the key event isn't sent through to the form even when KeyPreview is set to True.
To prevent this you can subClass a control and create an overridden IsInputKey method that returns true for whatever keys you need:
VB.NET:
Protected Overrides Function IsInputKey(ByVal keyData As _
  System.Windows.Forms.Keys) As Boolean
    Select Case keyData
        Case Keys.Up, Keys.Down, Keys.Left, Keys.Right, Keys.Enter, Keys.Tab
            Return True
    End Select
End Function
So in your case you would have to subclass each control that you don't want to react to the arrow keys. There may be another soultion, I'll have to give it more thought.

To solve #1 above, you could create a subclassed form control and override its IsInputKey (since the form has as its base control the System.Windows.Forms.Control class).

...and there's no point diggin beyond to look at my Unit class structures, collections, and code for drawing the collection. The problem isn't in there...
I don't think you can say this for certain.
I'm not sure why the nudge procedure eventually bubbles through to the form, I'll have to look into that further as well.
 
KeyPreview only works for InputKeys. Certain keys (such as the arrow keys, Tab, Enter, ...) are considered "special" keys and require preprocessing. Normally, these keys are used to navigate through the controls on a form that can receive focus (arrow and Tab keys) or to perform the default method of the control (Ex.: the Enter key will perform the click event of a button).
All classes based on the System.Windows.Forms.Control have a Protected Overridable method named IsInputKey which returns true if the specified key is a regular input key; otherwise false. If it returns false, the key event isn't sent through to the form even when KeyPreview is set to True.
To prevent this you can subClass a control and create an overridden IsInputKey method that returns true for whatever keys you need:
VB.NET:
Protected Overrides Function IsInputKey(ByVal keyData As _
  System.Windows.Forms.Keys) As Boolean
    Select Case keyData
        Case Keys.Up, Keys.Down, Keys.Left, Keys.Right, Keys.Enter, Keys.Tab
            Return True
    End Select
End Function
So in your case you would have to subclass each control that you don't want to react to the arrow keys. There may be another soultion, I'll have to give it more thought.

To solve #1 above, you could create a subclassed form control and override its IsInputKey (since the form has as its base control the System.Windows.Forms.Control class).

Thanks for that, I will try to incorporate it and post back. I knew there was something extra involved, I just haven't been able to find anything... I appreciate your input! This has been a nagging problem in my application.


I don't think you can say this for certain.
Yeah... in this case, I'm sure I can say it.

I'm not sure why the nudge procedure eventually bubbles through to the form, I'll have to look into that further as well.

Few posts up I mentioned that it bubbles thru the same exact way if you just drop a textbox on a form, and set tabstop to false. No need to focus on my nudge, as I mentioned, same exact thing happens if you just have it display a msgbox. On first press, the arrowkey will activate the textbox, once textbox has focus, the arrowkeys registers with the keydown.
 
Last edited:
OK, you're the expert :)

Hey, not trying to bite the hand that feeds...

You're the first glimmer of light I've had on this one. I just know what the underlying code does, it's not worthy of additional attention or time wasted.

If you want to explore a bit, I've isolated the problem and provided a very simple example that I need to overcome.

I'm hoping I'll be able to adapt your suggestions to ignore the 3 or 4 textboxes that are occupying my form.

I did find some AcceptsTab and AcceptsReturn properties but nothing to easily disregard the ArrowKeys. :confused:
 
Last edited:
Alright, I managed something that works pretty well for my needs. Bit of an endaround solution but allows me to avoid activating the textboxes with the arrowkeys.

I ended up using MouseMove of the userform and display panel to toggle the ReadOnly property for those textboxes. I then added some code to the offending textbox Enter event to test if it's read only and kick it to a hidden textbox to chew on the arrowkey in a hidden location if true.

Yeah, it's a tad hacky, and still not quite perfect. The arrowkeys always register on the second keypress, but for my GDI graphics nudging, I can live with that. Long as those textboxes aren't activated.


My thanks to:

Paszt and Juggalo for your ideas and suggestions. I'll continue to noodle on the idea of subclassing the form and/or various controls and using that override suggestion. Certainly sounds like the more proper route...
 
Back
Top