Why does MessageBox has an affect on SuppressKeyPress? - vb.net

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?

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.

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.)

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.
Use of Application.DoEvents()

Related

Why does windows play a beep sound on KeyDown, but not on DoubleClick?

I hope this is going to be a real quick question:
I have a TreeView on a Windows form.
I run this code to open directories, displayed in a tree view:
Private Sub OpenFolder()
Try
System.Diagnostics.Process.Start(SelectedDir)
Catch ex As Exception
MessageBox.Show("Mappen " & SelectedDir & " kan ikke åbnes!")
End Try
End Sub
When I call OpenFolder() from the KeyDown event:
Private Sub TreeViewDir_KeyDown(sender As Object, e As KeyEventArgs) Handles TreeViewDir.KeyDown
If e.KeyCode = Keys.Enter Then
OpenFolder()
e.SuppressKeyPress = True
ElseIf e.KeyCode = Keys.Delete Then
DeleteFolder()
e.SuppressKeyPress = True
End If
End Sub
..I get a windows error sound. But no error message. What is driving me up the walls, is that this sub fires the function without any problems at all.
Private Sub TreeViewDir_DoubleClick(sender As Object, e As EventArgs) Handles TreeViewDir.DoubleClick
OpenFolder()
End Sub
The error sound plays when the folder opens, but again, only on KeyDown. Can someone tell me why this happens only on the KeyDown event and what I'm doing wrong here?
First, let me point out that your OpenFolder() method isn't responsible for that beep sound, the KeyPress event is. This is a standard behavior of Windows when a key is pressed where it has no job to do.
Now, setting SuppressKeyPress to true, should, in fact, prevent the KeyPress event from firing and therefore, no beep sound should be played. However, in some cases when you execute some code in the KeyDown event, it takes time for the keystroke to be suppressed and therefore causing the beep sound.
To get around this, you can simply subscribe to the KeyPress event and set e.Handled to true:
Private Sub TreeViewDir_KeyPress(sender As Object, e As KeyPressEventArgs) Handles TreeViewDir.KeyPress
If e.KeyChar = ChrW(Keys.Enter) Then e.Handled = True
End Sub
Another solution, if you don't want to use KeyDown, is to allow some time for the keystroke to be suppressed, by delaying the execution of your method:
Private Async Sub TreeViewDir_KeyDown(sender As Object, e As KeyEventArgs) Handles TreeViewDir.KeyDown
' ^^^^^ ⟸ Don't forget the Async keyword.
If e.KeyCode = Keys.Enter Then
e.SuppressKeyPress = True ' This is first
Await Task.Delay(100) ' Followed by a small delay
OpenFolder() ' Then call the method.
End If
End Sub

how to create button shortcuts in vbnet

I tried creating keyboard shortcuts for my buttons.
Here is my code
Private Sub form_main_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles Me.KeyPress
If Keys.ControlKey + Keys.N Then
'btn_add.PerformClick()
addentry()
ElseIf Keys.ControlKey + Keys.E Then
'btn_edit.PerformClick()
editentry()
End If
End Sub
The problem is even when I press other buttons the function is still called. I also tried using form keydown property but the result is still the same.
additional info:
the functions addentry and editentry will just call the form_addedit
btn_add will call for addentry
btn_edit will call for editentry
First of all Keys.*** is just an enumeration. Every entry in it is just a number representing a key code. So you are currently just adding numbers together.
Keys.ControlKey is 17 and Keys.N is 78, so you're literally writing:
If 17 + 78 Then
Which will always return True because it's greater than 0.
To do what you ask you must check which key was pressed by checking the event arguments (EventArgs) passed to the event.
But since you are using the KeyPress event you cannot get the key enumeration out of the event args, so I recommend you to use the KeyDown event instead.
Private Sub form_main_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles Me.KeyDown
If e.Control = True AndAlso e.KeyCode = Keys.N Then
addentry()
ElseIf e.Control = True AndAlso e.KeyCode = Keys.E Then
editentry()
End If
End Sub
If you put an ampersand in the .Text property of the button, Alt+key will fire the button, for example B&utton1 will fire with Alt+u.

On key release handler

So, what I'm trying to do is run a piece of code when a button is pressed (I'll use the right-arrow key for the time being), and keep running it in a loop until that key is released.
So, my current code looks something like this (I've simplified it, because it's using SerialPorts, and is not easy to understand code):
Private Sub Form1_KeyDown(ByVal sender As System.Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles MyBase.KeyDown
Select Case e.KeyCode
Case Keys.Right
Do Until (CODE LOOKING FOR RIGHT-ARROW KEY RELEASE)
SerialPort1.Write("right")
Loop
e.Handled = True
SerialPort1.Close()
End Sub
Ultimately, what I'm trying to do is control and arduino through the serial port function. I've wired it up to a cheap rc car (I usually have a few arduino projects on the go), and have code which allows me to control it by arrow keys in the IDE itself.
However, I have limited VB.NET knowledge (I've only been at it a few years), so I'm kinda struggling along with the help of Google, which was a god end while trying to work out how to keep the serialport open (it kept closing due to a logic error in my code).
The goal is that once I can control it by arrow keys in VB.NET, I can set up a more advanced program which will allow pre-programming routes (for example, I can try to "teach" the car sets of instructions so that I can get it to automatically follow a circuit, and load various pre-programmed routes into it). I'd also plan an on-screen gui to show what button was pressed, and whatever else I think of.
Also, given this is a prototype, I intend to eventually hook up the arduino and VB program to my hobby grade rc, and see if I can get the computer program, with pre-programmed isntructions, to beat my round my track.
Well, after that little essay, I guess it's just thanks in advance, and any advice in other areas of my project is also appreciated =)
Why make this complicated. It looks like you want to send something as long as the key is down, and then close the port when it is released.
Private Sub Form1_KeyDown(sender As Object, e As KeyEventArgs) Handles Me.KeyDown
If e.KeyCode = Keys.Right AndAlso keydelay >= 1 Then
Debug.WriteLine("DWN") 'SerialPort1.Write("right")
e.Handled = True
ElseIf e.KeyCode = Keys.Right Then
keydelay += 1
End If
End Sub
Dim keydelay As Integer = 0
Private Sub Form1_KeyUp(sender As Object, e As KeyEventArgs) Handles Me.KeyUp
If e.KeyCode = Keys.Right Then
keydelay = 0
Debug.WriteLine("UP") 'SerialPort1.Close()
End If
End Sub

How to prevent special characters in datagridview using vb.net

I have a windows forms vb.net program that uses a datagridview. I'm trying to find a way to prevent a user from entering special characters (e.g. $,#,!,#,%,^,&) in my datagridview. When the user inputs a special character I have an approprioate message box appear explaining their mistake, then I provide them a default value. I have everything working except a way to prevent the special character or symbols. I'm thinking something like this has to work, but I can't seem to find any way of preventing this sort of entry:
If (columnindex = 0) Then 'checking value for column 1 only
Dim cellString = DataGridView1.Rows(rowindex).Cells(columnindex).value
If cellString String.IsSymbol(cellString) = true Then
MessageBox.Show("Special Characters Not Allowed")
End If
DataGridView1.Rows(rowindex).Cells(columnindex).value = "Default Value"
Exit Sub
End If
You can use the EditingControlShowing event to register a KeyPress function for the input box.
Private Sub YourDataGridView_EditingControlShowing(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewEditingControlShowingEventArgs) Handles YourDataGridView.EditingControlShowing
Try
RemoveHandler e.Control.KeyPress, AddressOf YourFunctionToPreventSpecialCharacters
Catch ex As Exception
End Try
If Me.dgvTableViewer.CurrentCell.ColumnIndex = YourDataGridView.Columns("YourColumn").Index Then
AddHandler e.Control.KeyPress, AddressOf YourFunctionToPreventSpecialCharacters
End If
End Sub
Try and Put this in the DataGridView's keydown event:
Private Sub DataGridView1_KeyDown(sender As Object, e As System.Windows.Forms.KeyEventArgs) Handles DataGridView1.KeyDown
Select Case e.KeyCode
Case Keys.D0 To Keys.D9 And e.Shift
MsgBox("NOPE.")
e.SuppressKeyPress = True
End Select
End Sub
That basically checks if the keypress is coming from the 0-9 keys on your computer and also if you are holding the SHIFT Key. If it is then it displays a Msgbox and Suppresses the keypress. This blocks chars on your keyboard's 0-9 shift !##$%^&*(). You can edit this like
Case Keys.A
e.Suppress ...
Msgbox ... etc

Multiple Keyboard Shortcuts

I am using the following code to try and get Ctrl+S to press a toolstrip button:
Private Sub take_register_KeyDown(ByVal sender As Object, _
ByVal e As System.Windows.Forms.KeyEventArgs) Handles Me.KeyDown
If e.KeyCode = Keys.S And Keys.Control Then
ToolStripButton20.PerformClick()
End If
End Sub
I am a newbie at this, so I dont understand millions of lines of coding, so can you please keep it as simple as possible :-) .
Total guesswork here since there is no actual question. First, in order to get something like that work, you need to set KeyPreview = True for the form. Next, you probably want to use the KeyDown event instead of KeyPress:
Private Sub Form1_KeyDown(...)
' when possible use AndAlso for speed and to avoid some errors in
' some situations. if e.Control is False, the second part wont be evaluated.
If e.Control AndAlso e.KeyCode = Keys.S Then
ToolStripButton20.PerformClick()
End If
End Sub
To repeat: you can simply assign a shortcut key combo to the menu object in the designer and let .NET do all the work. ...and I don't know where "multiple" comes in to play unless Ctrl+S counts as multiple somehow.