2

Here is a peculiar situation in vb. I was messing the with SuppressKeyPress property and I found out something strange.

The Situation

Lets say I have a text box called txtName, and I want the name to be without any numbers, and when a number is inserted, a MessageBox will pop out and report an error.

Private Sub TextBox1_KeyDown(sender As Object, e As System.Windows.Forms.KeyEventArgs) Handles txtName.KeyDown
    If e.KeyCode >= Keys.D0 And e.KeyCode <= Keys.D9 And _
e.Modifiers <> Keys.Shift Then
        e.SuppressKeyPress = True
        MsgBox("Error - A Number has been pressed")
        'The number appeared in the text box.
    End If
End Sub

In this case, for some strange reason, if I type a number, it will be written in the text box, although I suppressed the keypress.

What I found out is that if I remove the MsgBox line, the number will not appear in the text box.

Private Sub TextBox1_KeyDown(sender As Object, e As System.Windows.Forms.KeyEventArgs) Handles txtName.KeyDown
    If e.KeyCode >= Keys.D0 And e.KeyCode <= Keys.D9 And _
e.Modifiers <> Keys.Shift Then
        e.SuppressKeyPress = True
        'The number did not appear in the text box.
    End If
End Sub

Question

What is going on? Why the MsgBox "allows" the key to be pressed? Why it has any effect on the SuppressKeyPress property?

Eminem
  • 870
  • 5
  • 20
  • You need to handle the `KeyPress` event. For more info, see: [Setting e.Handled in the KeyDown event has no effect in Visual Basic .NET or in Visual Basic 2005](http://support.microsoft.com/kb/326442) | **Summary:** *This behavior is by design.* – Bjørn-Roger Kringsjå Aug 01 '14 at 17:25
  • But without the MsgBox, the KeyDown event does has an effect, because it prevents numbers to be written... – Eminem Aug 01 '14 at 17:35

3 Answers3

3

This is a pretty typical side-effect of using MessageBox, it can cause lots of tricky to diagnose problems. The SuppressKeyPress property is implemented by searching the message queue for any keypress events and removing them. But that can happen only after your event handler completes.

Trouble is, it isn't completing any time soon. Your MsgBox() call is taking over and it starts pumping a message loop by itself. Like dialogs do, the equivalent of calling the infamous DoEvents() method. And it will readily dispatch the pending messages in the message queue, including those keypress messages that were supposed to be suppressed.

A band-aid for such a problem is to display the message box later, after the event handling is completed and Winforms had a chance to purge the keypress messages. Elegantly done by using the Control.BeginInvoke() method:

Private Sub TextBox1_KeyDown(sender As Object, e As KeyEventArgs) Handles TextBox1.KeyDown
    If e.KeyCode >= Keys.D0 And e.KeyCode <= Keys.D9 And e.Modifiers <> Keys.Shift Then
        e.SuppressKeyPress = True
        Me.BeginInvoke(New Action(Sub() MsgBox("Error - A Number has been pressed")))
    End If
End Sub

But the real fix is to use the correct event. You should always use the KeyPress event instead for this kind of filtering. That also avoids the very painful dependency on the user's active keyboard layout that's always present when you use KeyDown. Fix:

Private Sub TextBox1_KeyPress(sender As Object, e As KeyPressEventArgs) Handles TextBox1.KeyPress
    If e.KeyChar >= "0"c AndAlso e.KeyChar <= "9"c Then
        e.Handled = True
        MsgBox("I don't like digits")
    End If
End Sub

But then again, don't use message boxes to slap the user for making a simple mistake.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
1

I found a little 'hack' for those who dont want to mess with Invoking methods, starting new threads manually etc.

My (old) code that didnt work was

Private Sub textEditKeyPress(ByVal sender As Object, ByVal e As KeyEventArgs)
    If e.KeyValue = 220 Then
        e.SuppressKeyPress = True
        MessageBox.Show("\ not allowed. Use / instead.")
    End If
End Sub

By changing the code to

Private Async Sub tEditDropBoxFolderName_EditValueChanged(ByVal sender As Object, ByVal e As KeyEventArgs)
    If e.KeyValue = 220 Then
        e.SuppressKeyPress = True
        Await Task.Delay(100)
        MessageBox.Show("\ not allowed. Use / instead.")
    End If
End Sub

Everything works fine, and i havent found any side effects by using this.

(Code was translated by c#, maybe it needs some modifications for vb.)

0

So I tested your code and I can reproduce this behavior. I think the reason as to why this is happening is because the MsgBox (a modal dialog) will call Application.DoEvents resulting in the message being processed.

By replacing

MsgBox("Error - A Number has been pressed")

with

Application.DoEvents

you'll get the same result.


You should read Hans Passant's answer in the following SO post. He provides a good explanation about the relationship between ShowDialog (ref. MsgBox) and DoEvents.

Community
  • 1
  • 1
Bjørn-Roger Kringsjå
  • 9,849
  • 6
  • 36
  • 64