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:

XL Jedi

Active member
Joined
Apr 2, 2007
Messages
44
Location
Florida
Programming Experience
10+
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.
 

JuggaloBrotha

VB.NET Forum Moderator
Staff member
Joined
Jun 3, 2004
Messages
4,524
Location
Lansing, MI; USA
Programming Experience
10+
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
 

XL Jedi

Active member
Joined
Apr 2, 2007
Messages
44
Location
Florida
Programming Experience
10+
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:
 

JuggaloBrotha

VB.NET Forum Moderator
Staff member
Joined
Jun 3, 2004
Messages
4,524
Location
Lansing, MI; USA
Programming Experience
10+
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.
 

XL Jedi

Active member
Joined
Apr 2, 2007
Messages
44
Location
Florida
Programming Experience
10+
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:

Paszt

Staff member
Joined
Jun 3, 2004
Messages
1,500
Location
Raleigh, NC - USA
Programming Experience
Beginner
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.
 

XL Jedi

Active member
Joined
Apr 2, 2007
Messages
44
Location
Florida
Programming Experience
10+
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:

XL Jedi

Active member
Joined
Apr 2, 2007
Messages
44
Location
Florida
Programming Experience
10+
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:

XL Jedi

Active member
Joined
Apr 2, 2007
Messages
44
Location
Florida
Programming Experience
10+
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...
 

JohnH

VB.NET Forum Moderator
Staff member
Joined
Dec 17, 2005
Messages
15,561
Location
Norway
Programming Experience
10+

XL Jedi

Active member
Joined
Apr 2, 2007
Messages
44
Location
Florida
Programming Experience
10+
I don't fully understand your scenario with form/controls and the order of events so I have no particular suggestion.

Thanks for the link John! I'll review that later today... I have a pretty strong programming background but I find myself in this vb.net transition phase where I can program the graphics and underlying engine, but hooking that engine into the display controls has been a tad frustrating, but I'm gradually overcoming it.

There's a picture of my app, with the 4 textboxes on this thread...
http://www.vbdotnetforums.com/showthread.php?t=19416

You've already been a great help with your TextRenderingHint suggestion.

I'm plotting in the main window, but since the program is heavily graphic oriented, there isn't much need for keyboard data input. When I plot these gadgets I can click to activate and move em around, but I also need to be able to nudge them for fine tuning. That's where the arrowkeys were activating my scaling textboxes outside of the plotting area... which has been very irritating.
 

Paszt

Staff member
Joined
Jun 3, 2004
Messages
1,500
Location
Raleigh, NC - USA
Programming Experience
Beginner
OK, I've re-read your posts and I think I kind of understand. You have graphic shapes on some sort of control that when selected should move according to the arrow key pressed. The problem is that the arrow keys are moving the focus to the textboxes. Sound correct?
What is the control that the graphic shapes are being drawn on? I'll assume it's a usercontrol. Regardless of what type of control it is, I believe you can solve your problem by subclassing that control. The subclassed control will be very simple, implementing the original control type and containing only the overridden IsInputKey procedure I posted above (you can of course leave out the Enter and Tab key if you wish to use those as originally designed). Then you can simply replace the original control with the subclassed control.

I'm a little confused though. Shouldn't the control that the shapes are on be handling the key processing itself and not the form? It seems that would be a more object-oriented approach. But I don't fully understand how you have implemented the application.

You have mentioned userform several times. I'm not familiar with the term userform. Do you mean the System.Windows.Forms.Form class?
 

XL Jedi

Active member
Joined
Apr 2, 2007
Messages
44
Location
Florida
Programming Experience
10+
OK, I've re-read your posts and I think I kind of understand. You have graphic shapes on some sort of control that when selected should move according to the arrow key pressed. The problem is that the arrow keys are moving the focus to the textboxes. Sound correct?
What is the control that the graphic shapes are being drawn on? I'll assume it's a usercontrol. Regardless of what type of control it is, I believe you can solve your problem by subclassing that control. The subclassed control will be very simple, implementing the original control type and containing only the overridden IsInputKey procedure I posted above (you can of course leave out the Enter and Tab key if you wish to use those as originally designed). Then you can simply replace the original control with the subclassed control.

I'm a little confused though. Shouldn't the control that the shapes are on be handling the key processing itself and not the form? It seems that would be a more object-oriented approach. But I don't fully understand how you have implemented the application.

You have mentioned userform several times. I'm not familiar with the term userform. Do you mean the System.Windows.Forms.Form class?

I've been writing code for over 10 years in VBA... yeah they're forms just used to calling em UserForms in VBA. I've got some stuff now that I'd like to migrate to standalone apps and visual studio was free and after playing with it a bit I like the IDE and all the gadgets exposed to vb.net, the GDI is a fun sandbox to play in.

I've implemented the graphics as follows...

I do all my drawing via the GDI directly to a bitmap array object in memory. I use the mousemove event of the picturebox to update the display by setting the picturebox.image equal to bitmap array Layer1. I keep a background image in Layer0 and use the picturebox.backgroundimage for that part.

I think to answer your question though it's the picturebox that I'm drawing on. The picturebox free-floats within a panel. So I've got a form, containing a panel, containing a picturebox, I'm drawing on the picturebox and using it's mousemove event to redraw.

So you think maybe subclassing the picturebox then?
 

Paszt

Staff member
Joined
Jun 3, 2004
Messages
1,500
Location
Raleigh, NC - USA
Programming Experience
Beginner
Oh, I see. The problem is that the picturebox control can not receive the focus.
I think the best solution would be to create a usercontrol containing the panel and picturebox. Of course this will lead to other concerns.
 

XL Jedi

Active member
Joined
Apr 2, 2007
Messages
44
Location
Florida
Programming Experience
10+
Oh, I see. The problem is that the picturebox control can not receive the focus.
I think the best solution would be to create a usercontrol containing the panel and picturebox. Of course this will lead to other concerns.

That sounds logical... I haven't played with creating usercontrols yet. I've seen a couple interesting examples. ...and I can see where further study in that direction could probably benefit my project.

I appreciate the diagnosis and suggestions for further study.
 
Top Bottom